ZJOI2017 树状数组
题目
思路
研究代码发现,她的树状数组实际上询问的是\([l-1,r-1]\)的抑或和,所以题目的询问实际上就可以转化为\(l-1\)上的元素与\(r\)上的元素相同的概率。
一开始想的是,线段树维护每一个元素是\(1\)的概率是多少,然后进行数学运算,得到答案,然后发现,两点之间的概率不是相互独立的。
比如说修改了\([l,r]\)的区间,那么如果这次修改的结果是\(l\),那么\(r\)在此次收到的影响就必然是\(0\),但是如果按照上文的思路,我们实际上对于这两个数都施加了影响,这显然是不对的。
所以对于查询的点对而言,暴力的写法就是循环一遍每一个询问,然后具体问题具体分析。
具体问题具体分析是马克思主义活的灵魂。 ------列宁《共产主义》
struct P50{
struct node{
int l,r;
}A[3005];
int acnt;
void solve(){
acnt=0;
for(int i=1,op,l,r;i<=m;i++){
scanf("%d%d%d",&op,&l,&r);
if(op==1){A[++acnt]=(node){l,r};}
else {
LL sa=1;
int ql,qr;
if(l!=1){
ql=l-1,qr=r;
for(int j=1;j<=acnt;j++){
L=A[j].l,R=A[j].r;
if(cont(ql)&&cont(qr)){
sa=((sa*calc(R-L-1,R-L+1))%mod+(1-sa+mod)*calc(2,R-L+1)%mod)%mod;
}
else if(cont(ql)||cont(qr)){
sa=((sa*calc(R-L,R-L+1))%mod+(1-sa+mod)*calc(1,R-L+1)%mod)%mod;
}
}
}
else {
for(int j=1;j<=acnt;j++){
L=A[j].l,R=A[j].r;
if(cont(r)){
sa=((sa*calc(1,R-L+1))%mod+(1-sa+mod)*calc(R-L,R-L+1)%mod)%mod;
}
else sa=(1-sa+mod)%mod;
}
}
printf("%lld\n",sa);
}
}
}
}p50;
然后怼上数据结构进行优化,不难发现,这是一个二维的模型,所以树套树,CDQ+线段树,二维线段树
这里以二维线段树为例。
这里维护的是点对之间的关系,
对于二维上的一个矩形,下标\([l,r]\)所对应的点的权值就表示的是\(l\)与\(r\)不同的概率。
然后此处仍然延续50分的分类,对于矩形上的点用线段树施加影响就行了。
然后对于\(l=1\)的点,存在一组特判。
\(sum[r]-sum[l-1]\)在错误的代码中,\(sum[l-1]\)会被直接特判为0。
所以她的询问合法的情况就变成了:
\[正确: [l,r]\\
错误: [r,n]\\
sum[n]-sum[r-1]=sum[r]-sum[l-1](l=1)\\
sum[n]=val[r]\\
\]
只需要知道\(val[r]\)的值,然后根据询问的次数(每次询问\(sum[n]\)必定改变)判断二者是否相等就行了。
代码
#include<bits/stdc++.h>
#define LL long long
#define M 100005
using namespace std;
const LL mod=998244353;
int n,m;
LL qkpow(LL a,LL b){
LL res=1;
while(b){
if(b&1)res=res*a%mod;
a=a*a%mod;
b>>=1;
}
return res;
}
LL calc(LL x,LL y){
return x*qkpow(y,mod-2)%mod;
}
LL add(LL a,LL b){
return (a*(mod+1-b)+b*(mod+1-a))%mod;
}
int now;
int Lson[M*400],Rson[M*400],sum[M*400],ver[M<<2],tt;
void AddY(int &p,int l,int r,int L,int R){
if(!p)p=++tt;
if(l==L&&r==R){sum[p]=add(sum[p],now);return;}
int mid=(l+r)>>1;
if(R<=mid)AddY(Lson[p],l,mid,L,R);
else if(L>mid)AddY(Rson[p],mid+1,r,L,R);
else {
AddY(Lson[p],l,mid,L,mid);
AddY(Rson[p],mid+1,r,mid+1,R);
}
}
void AddX(int p,int l,int r,int lX,int rX,int lY,int rY){
// cout<<l<<' '<<r<<' '<<lX<<' '<<rX<<endl;
if(l==lX&&r==rX){
AddY(ver[p],0,n,lY,rY);
return;
}
int mid=(l+r)>>1;
if(rX<=mid)AddX(p<<1,l,mid,lX,rX,lY,rY);
else if(lX>mid)AddX(p<<1|1,mid+1,r,lX,rX,lY,rY);
else {
AddX(p<<1,l,mid,lX,mid,lY,rY);
AddX(p<<1|1,mid+1,r,mid+1,rX,lY,rY);
}
}
int queryY(int x,int l,int r,int Y) {
if(l==r) return sum[x];
int mid=(l+r)>>1,res=sum[x];
if(Y<=mid) res=add(res,queryY(Lson[x],l,mid,Y));
else res=add(res,queryY(Rson[x],mid+1,r,Y));
return res;
}
int queryX(int x,int l,int r,int X,int Y) {
if(l==r) return queryY(ver[x],0,n,Y);
int mid=(l+r)>>1,res=queryY(ver[x],0,n,Y);
if(X<=mid)res=add(res,queryX(x<<1,l,mid,X,Y));
else res=add(res,queryX(x<<1|1,mid+1,r,X,Y));
return res;
}
int main(){
// freopen("bit.in","r",stdin);
// freopen("bit.out","w",stdout);
scanf("%d%d",&n,&m);
int ct=0;
while(m--){
int op,l,r;
scanf("%d%d%d",&op,&l,&r);
if(op==1){
now=calc(2,r-l+1);
AddX(1,0,n,l,r,l,r);
now=calc(1,r-l+1);
if(r<n)AddX(1,0,n,l,r,r+1,n);
AddX(1,0,n,0,l-1,l,r);
ct=!ct;
}
else {
int res=(mod+1-queryX(1,0,n,l-1,r))%mod;
if(l==1&&ct)res=(1-res+mod)%mod;
printf("%d\n",res);
}
}
return 0;
}