「BJOI2018」二进制

题面描述

pupil 发现对于一个十进制数,无论怎么将其的数字重新排列,均不影响其是不是 \(3\) 的倍数。他想研究对于二进制,是否也有类似的性质。
于是他生成了一个长为 \(n\) 的二进制串,希望你对于这个二进制串的一个子区间,能求出其有多少位置不同的连续子串,满足在重新排列后(可包含前导 \(0\))是一个 \(3\) 的倍数。两个位置不同的子区间指开始位置不同或结束位置不同。
由于他想尝试尽量多的情况,他有时会修改串中的一个位置,并且会进行多次询问。
对于 \(20\%\) 的数据,\(1 \leq n,m \leq 100\)
对于 \(50\%\) 的数据,\(1 \leq n,m \leq 5000\)
对于 \(100\%\) 的数据,\(1 \leq n,m \leq 100000\)\(l \leq r\)

题解

用这篇文章来提醒自己在考试时合理分配时间并牢记PhantasmDragon大佬欠大家女装一次
P.S.:经本人确认已女装
反向考虑,发现一个01串不行当且仅当满足以下条件:
1.\(‘1’\)的个数为奇数
2.\(‘1’\)的个数为1或\(‘0’\)的个数小于2
感觉到要用线段树,考虑如何合并相邻两区间,得到更大区间的答案。
只需要考虑出现跨过左右两边的串就行了,因此需要多维护一个区间从左/右数,经过第1/2个\(‘0’\)/\(‘1’\)之前的数的个数,为了维护这些可能还要维护区间长度。
发现可能会算重,即串\(“01”\)\(“10”\)会被统计两次。去掉就好了。
所以这算动态DP???

代码

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int Q=1<<17,P=1<<20;
#define ll long long
int a[Q];
struct dt{
    int l11,l12;
    int r11,r12;
    int l01,l02;
    int r01,r02;
    int len;
    ll f;
}p[Q<<2],ch[2];
void Do()
{
    ch[0]=(dt){1,0,1,0,0,0,0,0,1,0};
    ch[1]=(dt){0,1,0,1,1,0,1,0,1,1};
}
// 0->even 1->odd
int G(int ty,int x)
{return (x+ty)>>1;}
int G(int ty,int x,int y)
{return G(ty,y)-G(ty,x-1);}
dt operator+(dt a,dt b)
{
    dt tmp;
    if(a.l11!=a.len){
        tmp.l11=a.l11;
        if(a.l11+a.l12!=a.len)tmp.l12=a.l12;
        else tmp.l12=a.l12+b.l11;
    }
    else{
        tmp.l11=a.len+b.l11;
        tmp.l12=b.l12;
    }
    if(b.r11!=b.len){
        tmp.r11=b.r11;
        if(b.r11+b.r12!=b.len)tmp.r12=b.r12;
        else tmp.r12=b.r12+a.r11;
    }
    else{
        tmp.r11=b.len+a.r11;
        tmp.r12=a.r12;
    }
    if(a.l01<a.len){
        tmp.l01=a.l01;
        if(a.l01+a.l02+1<a.len)tmp.l02=a.l02;
        else tmp.l02=a.l02+b.l01;
    }
    else{
        tmp.l01=a.len+b.l01;
        tmp.l02=b.l02;
    }
    //
    if(b.r01<b.len){
        tmp.r01=b.r01;
        if(b.r01+b.r02+1<b.len)tmp.r02=b.r02;
        else tmp.r02=b.r02+a.r01;
    }
    else{
        tmp.r01=b.len+a.r01;
        tmp.r02=a.r02;
    }
    tmp.len=a.len+b.len;
    tmp.f=a.f+b.f;
    tmp.f+=1LL*a.r11*b.l12+1LL*b.l11*a.r12;
    tmp.f+=1LL*G(0,a.r01)*G(1,b.l01)+1LL*G(1,a.r01)*G(0,b.l01);
    if(a.r01!=a.len)tmp.f+=1LL*G(0,a.r01,a.r01+a.r02)*G(1,b.l01)+1LL*G(1,a.r01,a.r01+a.r02)*G(0,b.l01);
    if(b.l01!=b.len)tmp.f+=1LL*G(0,b.l01,b.l01+b.l02)*G(1,a.r01)+1LL*G(1,b.l01,b.l01+b.l02)*G(0,a.r01);
    if((a.r11==0)^(b.l11==0))tmp.f--;
    return tmp;
}
#define mid ((l+r)>>1)
#define ls (now<<1)
#define rs (now<<1|1)
void upd(int now)
{p[now]=p[ls]+p[rs];}
void Init(int now,int l,int r)
{
    if(l==r){
        p[now]=ch[a[l]];
        return;
    }
    Init(ls,l,mid),Init(rs,mid+1,r);
    upd(now);
}
void MDF(int now,int l,int r,int x)
{
    if(l==r){
        p[now]=ch[a[x]];
        return;
    }
    if(x<=mid)MDF(ls,l,mid,x);
    else MDF(rs,mid+1,r,x);
    upd(now);
}
dt Gans(int now,int l,int r,int x,int y)
{
    if(x<=l&&y>=r)return p[now];
    if(y<=mid)return Gans(ls,l,mid,x,y);
    if(x>mid)return Gans(rs,mid+1,r,x,y);
    return Gans(ls,l,mid,x,y)+Gans(rs,mid+1,r,x,y);
}
int main()
{
    Do();
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    Init(1,1,n);
    int m;
    scanf("%d",&m);
    for(int p=1;p<=m;p++){
        int ty,x,y;
        scanf("%d%d",&ty,&x);
        if(ty==1){
            a[x]^=1;
            MDF(1,1,n,x);
        }
        else{
            scanf("%d",&y);
            printf("%lld\n",1LL*(y-x+1)*(y-x+2)/2LL-Gans(1,1,n,x,y).f);
        }
    }
    return 0;
}
posted @ 2019-03-21 20:13 蒟蒻小果冻 阅读(...) 评论(...) 编辑 收藏