[ZJOI2017]树状数组

以后写题前还是要冷静分析清楚再动手啊.....

$ Think \ twice,code \ once$


传送门:here

大致题意:

有一个写挂的树状数组,$ query$的是后缀和,$ query(0)$时返回$ 0$

有$ m$次操作,

$ 1 \ L \ R$表示在$ L...R$中等概率选取一个点$ update$

$ 2 \ L \ R$表示求$ query(L,R)$和正确的树状数组$ query$在模$ 2$意义下相同的概率


先特判掉询问时$ L==1$的情况

发现相当于询问$ [L-1..n]-[R..n]-[1..R]+[1..L-1]=val[L-1]-val[R]$模$ 2$意义下的值

这题询问的两个端点$ L,R$不能独立考虑,因为一次$ update$只能选取一个点进行更新

考虑暴力

我们枚举一次$ query$前的所有$ update(L,R)$

如果$ query$的$ L-1$和$ R$均在$ update$区间内,则有$ \frac{2}{len}$的概率改变模$ 2$意义下的结果

否则若只有一个端点在$ update$区间内,则有$ \frac{1}{len}$的概率改变模$ 2$意义下的结果

我们用$ p1,p2$分别表示这次更新和更新前结果为$ 0$的概率,则有$ merge(p1,p2)=p1p2+(1-p1)(1-p2)$

可以令$ p1=1$然后每次求出$ p2$并进行合并


 

考虑用树套树维护这个东西

对于每一次查询$ L,R$相当于查询坐标$ (L-1,R)$上的结果

对于每一个$ change(1,L,R)$,我们给矩形内打上$ \frac{2}{len}$概率修改的标记,矩形外只包含$ L-1$或只包含$ R$的四块矩形打上$ \frac{2}{len}$概率修改的标记

由于查询的$(L-1,R)$保证$ L-1<R$,因此只包含$ L-1$或只包含$ R$的四块矩形里我们只需要修改两块以节省空间

由于单点查询可以轻松用标记永久化维护

洛谷上第$ 13$个点只有修改没有询问,可能需要卡一波空间

记得特判$ L=1$


 $ my \ code:$

#include<ctime>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#define p 998244353
#define rt register int
#define ll long long
using namespace std;
inline ll read(){
    ll x = 0; char zf = 1; char ch = getchar();
    while (ch != '-' && !isdigit(ch)) ch = getchar();
    if (ch == '-') zf = -1, ch = getchar();
    while (isdigit(ch)) x = x * 10 + ch - '0', ch = getchar(); return x * zf;
}
void write(ll y){if(y<0)putchar('-'),y=-y;if(y>9)write(y/10);putchar(y%10+48);}
void writeln(const ll y){write(y);putchar('\n');}
int i,j,k,m,n,x,y,z,cnt,Cnt;
ll inv[100010];
struct segment{
    int ls,rs,val;
}a[36000010];
int to[400010];
int Root,ans;
inline void merge(int &x,int y){
    x=(1ll*x*y+(1ll-x)*(1-y))%p;
}
void Insert(int &x,int L,int R,int L1,int R1,int gl){
    if(L>R1||R<L1)return;
    if(!x)x=++cnt,a[x].val=1;
    if(L>=L1&&R<=R1)return merge(a[x].val,gl);
    const int mid=L+R>>1;
    Insert(a[x].ls,L,mid,L1,R1,gl);
    Insert(a[x].rs,mid+1,R,L1,R1,gl);
}
void insert(int &x,int L,int R,int L1,int R1,int L2,int R2,int gl){
    if(L>R1||R<L1)return;if(!x)x=++Cnt;
    if(L>=L1&&R<=R1)return Insert(to[x],1,n,L2,R2,gl);
    const int mid=L+R>>1;
    insert(a[x].ls,L,mid,L1,R1,L2,R2,gl);
    insert(a[x].rs,mid+1,R,L1,R1,L2,R2,gl);
}
void Query(int x,int L,int R,int w){
    if(!x)return;merge(ans,a[x].val);
    const int mid=L+R>>1;
    if(w<=mid)Query(a[x].ls,L,mid,w);
    else Query(a[x].rs,mid+1,R,w);
}
void query(int x,int L,int R,int w1,int w2){
    if(!x)return;
    Query(to[x],1,n,w2);
    const int mid=L+R>>1;
    if(w1<=mid)query(a[x].ls,L,mid,w1,w2);
    else query(a[x].rs,mid+1,R,w1,w2);
}
int main(){
    n=read();m=read();int t=0;
    inv[1]=1;
    for(rt i=2;i<=n;i++)inv[i]=inv[p%i]*(p-p/i)%p;    
    cnt=400000;
    for(rt i=1;i<=m;i++){
        x=read();int L=read(),R=read();
        if(x==1){
            int len=R-L+1,v=1;t++;
            insert(Root,0,n,L,R,L,R,1-2*inv[len]);    
            insert(Root,0,n,L,R,R+1,n,1-inv[len]);        
            insert(Root,0,n,0,L-1,L,R,1-inv[len]);        
        }
        else{
            z=i;ans=1;query(Root,0,n,L-1,R);
            if(L==1&&(t&1))ans=(1-ans)%p;
            (ans+=p)%=p;
            writeln(ans);
        }
    }
    return 0;
}

 

posted @ 2018-10-29 13:46  Kananix  阅读(216)  评论(0编辑  收藏  举报

Contact with me