「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;
}