正题
解题思路:https://www.luogu.org/recordnew/lists?uid=52918&pid=P3870
题目大意
n个灯,操作[0,l,r][0,l,r][0,l,r]表示l∼rl\sim rl∼r的灯取反,操作[1,l,r][1,l,r][1,l,r]表示询问l∼rl\sim rl∼r之间有多少灯亮着。
解题思路
分块,对于每个块维护两个值sum,fsum,fsum,f。sum表示这个块有多少个开着的灯,f表示这个块是否取反。
由于有了f所以我们对于块就可以不用取反了。
code
#include<cstdio>
#include<cmath>
#define pos(x) (x-1)/sq+1
#define N 100010
#define T 500
using namespace std;
int n,m,l,r,x,sq,t;
bool a[N],f[T];
int L[T],R[T],sum[T];
void ycl()
{sq=t=sqrt(n);for(int i=1;i<=t;i++){L[i]=(i-1)*t+1;R[i]=i*t;}if(R[t]<n) t++,L[t]=R[t-1]+1,R[t]=n;
}
void change(int l,int r)
{int q=pos(l),p=pos(r);if(q==p){for(int i=l;i<=r;i++)sum[p]+=1-a[i]*2,a[i]=!a[i];}else{for(int i=q+1;i<=p-1;i++)f[i]=!f[i];for(int i=l;i<=R[q];i++)sum[q]+=1-a[i]*2,a[i]=!a[i];for(int i=L[p];i<=r;i++)sum[p]+=1-a[i]*2,a[i]=!a[i];}
}
int ask(int l,int r)
{int ans=0;int q=pos(l),p=pos(r);if(q==p){for(int i=l;i<=r;i++)ans+=a[i];if(f[p]) return r-l+1-ans;return ans;}else{for(int i=q+1;i<=p-1;i++)if(f[i]) ans+=R[i]-L[i]+1-sum[i];else ans+=sum[i];int k=0;for(int i=l;i<=R[q];i++)k+=a[i];if(f[q]) ans+=R[q]-l+1-k;else ans+=k;k=0;for(int i=L[p];i<=r;i++)k+=a[i];if(f[p]) ans+=r-L[p]+1-k;else ans+=k;}return ans;
}
int main()
{scanf("%d%d",&n,&m);ycl();for(int i=1;i<=m;i++){scanf("%d%d%d",&x,&l,&r);if(x) printf("%d\n",ask(l,r));else change(l,r);}
}