Luogu

LOJ

分析

二进制下每一位模 $3$ 的余数是 $1,2,1,2,\cdots$,也可以看成 $1,-1,1,-1,\cdots$。

假设有 $k$ 个 $1$,我们要将其分配到 $\lceil\frac{n}{2}\rceil$ 个 $1$ 和 $\lfloor\frac{n}{2}\rfloor$ 个 $-1$ 上,使得分配到的 $1$ 和 $-1$ 的和是 $3$ 的倍数。

如果 $k$ 是偶数,显然是可以的;否则我们会把 $k-3$ 个 $1$ 均匀分配,剩下的给 $1$(因为 $1$ 不会比 $-1$ 少)。

那么就是要有 $\frac{k+3}{2}\leq\lceil\frac{n}{2}\rceil$。综合一下可以得到:

  • 如果 $0$ 的个数 $\geq 3$ 且 $1$ 的个数 $\geq 2$,那么存在满足条件的重排方式;
  • 如果 $0$ 的个数为 $2$ 且 $k$ 为奇数,那么也存在满足条件的重排方式。

考虑不合法的情况,有

  • 只有一个 $1$ 的区间;
  • 出现了奇数个 $1$ 且 $0$ 的个数 $\leq 1$ 的区间。

为了不重复统计,给上面的加上限制条件:$0$ 的个数 $\geq 2$。

用线段树维护整个序列。对于每个节点,维护以下信息:

  • $dl_{0/1,0/1}$ 表示包含 $0/1$ 个 $0$、$1$ 有奇数 / 偶数个的前缀数,$dr_{0/1,0/1}$ 类似;
  • $fl_{0,1,2}$ 表示含有 $1$ 个 $1$、$0/1/\geq 2$ 个 $0$ 的前缀数,$fr_{0,1,2}$ 类似。
  • $l_0$ 表示最长的前缀 $0$ 的数量,$r_0$ 同理。
  • $s_0$ 表示 $0$ 的数量,$s_1$ 表示 $1$ 的数量。
  • $s$ 表示不合法的子区间的数量。

根据上面的结论合并即可合并两个区间。

代码

// ====================================
//   author: M_sea
//   website: https://m-sea-blog.com/
// ====================================
#include <bits/stdc++.h>
#define file(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
#define debug(...) fprintf(stderr,__VA_ARGS__)
using namespace std;
typedef long long ll;

int read() {
    int X=0,w=1; char c=getchar();
    while (c<'0'||c>'9') { if (c=='-') w=-1; c=getchar(); }
    while (c>='0'&&c<='9') X=X*10+c-'0',c=getchar();
    return X*w;
}

const int N=100000+10;

int n,Q,a[N];

#define ls (o<<1)
#define rs (o<<1|1)
struct node {
    ll dl[2][2],dr[2][2],fl[3],fr[3],s;
    int l0,r0,s0,s1;
    node() {
        memset(dl,0,sizeof(dl)),memset(dr,0,sizeof(dr));
        fl[0]=fl[1]=fl[2]=fr[0]=fr[1]=fr[2]=s=l0=r0=s0=s1=0;
    }
    node(int w) {
        *this=node();
        if (w) dl[0][1]=dr[0][1]=fl[0]=fr[0]=s=s1=1;
        else dl[1][0]=dr[1][0]=l0=r0=s0=1;
    }
} t[N<<2];
node operator +(node a,node b) {
    node c;
    for (int i=0;i<2;++i)
        for (int j=0;j<2;++j) {
            c.dl[i][j]+=a.dl[i][j],c.dr[i][j]+=b.dr[i][j];
            if (i>=a.s0) c.dl[i][j]+=b.dl[i-a.s0][j^(a.s1&1)];
            if (i>=b.s0) c.dr[i][j]+=a.dr[i-b.s0][j^(b.s1&1)];
        }
    for (int i=0;i<=2;++i) {
        c.fl[i]+=a.fl[i],c.fr[i]+=b.fr[i];
        if (!a.s1) c.fl[min(2,i+a.s0)]+=b.fl[i];
        if (!b.s1) c.fr[min(2,i+b.s0)]+=a.fr[i];
    }
    if (a.s1==1&&b.l0) ++c.fl[min(2,a.s0+b.l0)],c.fl[2]+=b.l0-1;
    if (b.s1==1&&a.r0) ++c.fr[min(2,b.s0+a.r0)],c.fr[2]+=a.r0-1;
    c.l0=a.s1?a.l0:a.s0+b.l0,c.r0=b.s1?b.r0:b.s0+a.r0;
    c.s0=a.s0+b.s0,c.s1=a.s1+b.s1;
    c.s=a.s+b.s;
    c.s+=a.dr[0][0]*(b.dl[0][1]+b.dl[1][1]);
    c.s+=a.dr[0][1]*(b.dl[0][0]+b.dl[1][0]);
    c.s+=a.dr[1][0]*b.dl[0][1];
    c.s+=a.dr[1][1]*b.dl[0][0];
    if (b.l0) c.s+=b.l0*(a.fr[0]+a.fr[1]+a.fr[2])-a.fr[0];
    if (a.r0) c.s+=a.r0*(b.fl[0]+b.fl[1]+b.fl[2])-b.fl[0];
    return c;
}
void pushup(int o) { t[o]=t[ls]+t[rs]; }
void build(int o,int l,int r) {
    if (l==r) { t[o]=node(a[l]); return; }
    int mid=(l+r)>>1;
    build(ls,l,mid),build(rs,mid+1,r);
    pushup(o);
}
void modify(int o,int l,int r,int p,int w) {
    if (l==r) { t[o]=node(w); return; }
    int mid=(l+r)>>1;
    if (p<=mid) modify(ls,l,mid,p,w);
    else modify(rs,mid+1,r,p,w);
    pushup(o);
}
node query(int o,int l,int r,int ql,int qr) {
    if (ql<=l&&r<=qr) return t[o];
    int mid=(l+r)>>1;
    if (qr<=mid) return query(ls,l,mid,ql,qr);
    else if (ql>mid) return query(rs,mid+1,r,ql,qr);
    else return query(ls,l,mid,ql,mid)+query(rs,mid+1,r,mid+1,qr);
}
#undef ls
#undef rs

int main() {
    n=read();
    for (int i=1;i<=n;++i) a[i]=read();
    build(1,1,n);
    Q=read();
    while (Q--) {
        int op=read();
        if (op==1) {
            int p=read(); a[p]^=1;
            modify(1,1,n,p,a[p]);
        } else {
            int l=read(),r=read();
            printf("%lld\n",1ll*(r-l+1)*(r-l+2)/2-query(1,1,n,l,r).s);
        }
    }
    return 0;
}
最后修改:2021 年 01 月 21 日 08 : 32 PM