[ZJOI2017] 树状数组

题目描述:

题目分析:

那么对于每个询问,如果l!=1,那么我们查询的其实是[l−1,r−1]这段区间。而[l−1,r−1]与[l,r]仅有l−1和r这两个元素有区别。所以我们每次询问就是问l−1和r

的修改次数在模2意义下是否相等。

那么我们可以把每个询问看成(l−1,r)

这个点,那么这就是个二维选点问题了,我们用树套树来维护。外层的树维护第一维坐标,内层的树维护第二维坐标。我们维护的值就是这个点的两个坐标修改次数在模2意义下相等的概率。

如何合并概率呢?当两个点被修改的概率分别为p1,p2时,两个点修改次数相等的概率是p=p1∗p2+(1−p1)∗(1−p2),这个式子也适用于合并操作。

当l=1时,情况类似,我们要求的就变成了对于r这个点,它的前缀和是否与它的后缀和相等。维护思想与l!=1的情况类似。

使用标记永久化进行维护

题目链接:

Luogu 3688

Ac 代码:

#include 
#include 
#include 
#include 
#define merge(p1,p2) ((p1*p2+(1-p1+mod)*(1-p2+mod))%mod)
#define int long long
const int mod=998244353;
const int maxm=100010;
int rt[maxm*4];
int n,m;
int ans;
namespace inside_seg{
    int ls[maxm*210],rs[maxm*210];
    int sum[maxm*210];
    int sz;
    void modify(int &now,int l,int r,int ql,int qr,int v)
    {
        if(!now) sum[now=++sz]=1;
        if(ql<=l&&r<=qr)
        {
            sum[now]=merge(sum[now],v);
            return;
        }
        int mid=(l+r)>>1;
        if(ql<=mid) modify(ls[now],l,mid,ql,qr,v);
        if(qr>mid) modify(rs[now],mid+1,r,ql,qr,v);
    }
    void ask(int now,int l,int r,int ind)
    {
        if(!now) return;
        ans=merge(ans,sum[now]);
        if(l>=r) return;
        int mid=(l+r)>>1;
        if(ind<=mid) ask(ls[now],l,mid,ind);
        else ask(rs[now],mid+1,r,ind);
    }
}
namespace external_seg{
    void modify(int now,int l,int r,int l1,int r1,int l2,int r2,int v)
    {
        if(l1<=l&&r<=r1)
        {
            inside_seg::modify(rt[now],0,n+1,l2,r2,v);
            return;
        }
        int mid=(l+r)>>1;
        if(l1<=mid) modify((now<<1),l,mid,l1,r1,l2,r2,v);
        if(r1>mid) modify((now<<1)|1,mid+1,r,l1,r1,l2,r2,v);
    }
    void ask(int now,int l,int r,int ind1,int ind2)
    {
        if(rt[now]) inside_seg::ask(rt[now],0,n+1,ind2);
        if(l>=r) return;
        int mid=(l+r)>>1;
        if(ind1<=mid) ask((now<<1),l,mid,ind1,ind2);
        else ask((now<<1)|1,mid+1,r,ind1,ind2);
    }
}
int fastpow(int x,int y)
{
    x%=mod;
    int ans=1;
    while(y)
    {
        if(y%2) ans=(ans*x)%mod;
        x=(x*x)%mod;
        y/=2;
    }
    return ans%mod;
}
main()
{
    //freopen("233.out","w",stdout);
    scanf("%lld%lld",&n,&m);
    for(int i=1;i<=m;i++)
    {
        int opt,l,r;
        scanf("%lld%lld%lld",&opt,&l,&r);
        if(opt==1)
        {
            int p=fastpow(r-l+1,mod-2);
            if(l>1) external_seg::modify(1,0,n,1,l-1,l,r,(1-p+mod)%mod);
            if(r1,0,n,l,r,r+1,n,(1-p+mod)%mod);
            int pp=p<<1; 
            if(pp>=mod) pp-=mod;
            external_seg::modify(1,0,n,l,r,l,r,(1-pp+mod)%mod);
            external_seg::modify(1,0,n,0,0,0,l-1,0);
            external_seg::modify(1,0,n,0,0,r+1,n+1,0);
            external_seg::modify(1,0,n,0,0,l,r,p);
        }
        else
        {
            ans=1;
            external_seg::ask(1,0,n,l-1,r);
            printf("%lld\n",ans%mod);
        }
    }
    return 0;
}

你可能感兴趣的:(题目分析,二维线段树,树套树)