1. 程式人生 > >ZJOI 2017 樹狀數組(線段樹套線段樹)

ZJOI 2017 樹狀數組(線段樹套線段樹)

需要 const -- long long 題意 原來 eof 二維線段樹 gcd

題意

http://uoj.ac/problem/291

思路

不難發現,九條カレン醬所寫的樹狀數組,在查詢區間 \([1,r]\) 的時候,其實在查詢後綴 \([r,n]\) ;在查詢 \([l,r](l\neq1)\) 的時候,則是在查詢 \([l-1,r-1]\) 。那麽在查詢 \([1,r]\) 的時候,只需要詢問 \(r\) 的前後綴異或是否相等;在查詢 \([l,r](l\neq 1)\) 的時候,只需要詢問 \(a[l-1],a[r]\) 是否相等。

考慮 \(O(n^2)\) 的暴力。我們把詢問分成上述的兩類。第一類詢問如果修改到了點 \(r\) ,則無影響,否則就是相等變不相等的轉化,分詢問區間蓋住 \(r\)

和不蓋住 \(r\) 兩種情況考慮。設原來相等的概率為 \(p\) ,再進行修改不影響的概率為 \(q\) ,那麽修改後相等的概率就是 \(pq+(1-p)(1-q)\) 。對於第二類詢問也是一樣的,分區間覆蓋 \(l-1\) 點和 \(r\) 點、覆蓋其中一個點、都不覆蓋三種情況考慮。代碼中有切了這一檔分,方便和正解對照。

我們可以同時維護住所有答案,然後只接回答詢問。用一個一維數據結構維護每個點 \(x\) 的前綴或者後綴是否相等,一個二維數據結構用來維護 \(a[x],a[y]\) 的值是否相等。修改和上面的暴力是同理的,是對一個區間(一維或二維)的點附上一個修改後相等的概率,對於修改顯然是交換結合都沒什麽關系。那這個一維數據結構選擇線段樹,二維數據結構選擇線段樹套線段樹即可。

二維線段樹比較好寫的寫法是靜點套動點,不過動點套動點也可以寫的。而且這道題其實空間是不夠的,但比較難卡,一般卡不滿。

代碼

#include<bits/stdc++.h>
#define FOR(i,x,y) for(int i=(x),i##END=(y);i<=i##END;++i)
#define DOR(i,x,y) for(int i=(x),i##END=(y);i>=i##END;--i)
template<typename T,typename _T>inline bool chk_min(T &x,const _T y){return y<x?x=y,1:0;}
template<typename T,typename _T>inline bool chk_max(T &x,const _T y){return x<y?x=y,1:0;}
typedef long long ll;
const int P=998244353;
const int N=1e5+5;
int op[N],ql[N],qr[N];
int n,m;
void exgcd(int a,int b,int &x,int &y)
{
    if(!b){x=1,y=0;return;}
    exgcd(b,a%b,y,x),y-=a/b*x;
}
int inv(int a)
{
    int x,y;
    exgcd(a,P,x,y);
    return (x%P+P)%P;
}
namespace Subtask1
{
    int merge(int x,int y)
    {
        return ((1ll*x*y+1ll*(1-x)*(1-y))%P+P)%P;
    }
    void Solve()
    {
        FOR(i,1,m)if(op[i]==2)
        {
            int l=ql[i],r=qr[i];
            int p=1;
            if(l==1)
            {
                FOR(j,1,i-1)if(op[j]==1)
                {
                    int len=qr[j]-ql[j]+1;
                    if(ql[j]<=r&&r<=qr[j])
                        p=merge(p,inv(len));
                    else p=merge(p,0);
                }
            }
            else
            {
                l--;
                FOR(j,1,i-1)if(op[j]==1)
                {
                    int len=qr[j]-ql[j]+1;
                    if(ql[j]<=l&&r<=qr[j])
                        p=merge(p,1ll*(len-2)*inv(len)%P);
                    else if((ql[j]<=l&&l<=qr[j])||(ql[j]<=r&&r<=qr[j]))
                        p=merge(p,1ll*(len-1)*inv(len)%P);
                }
            }
            printf("%d\n",p);
        }
    }
};

namespace Subtask2
{
    int merge(int x,int y)
    {
        return ((1ll*x*y+1ll*(1-x)*(1-y))%P+P)%P;
    }
    struct SegmentTree
    {
        int pw[N<<2];
        void build(int k,int l,int r)
        {
            pw[k]=1;
            if(l==r)return;
            int mid=(l+r)>>1;
            build(k<<1,l,mid);
            build(k<<1|1,mid+1,r);
        }
        void update(int k,int L,int R,int val,int l,int r)
        {
            if(L<=l&&r<=R)
            {
                pw[k]=merge(pw[k],val);
                return;
            }
            int mid=(l+r)>>1;
            if(L<=mid)update(k<<1,L,R,val,l,mid);
            if(R>mid)update(k<<1|1,L,R,val,mid+1,r);
        }
        int query(int k,int x,int l,int r)
        {
            if(l==r)return pw[k];
            int mid=(l+r)>>1;
            if(x<=mid)return merge(pw[k],query(k<<1,x,l,mid));
            else return merge(pw[k],query(k<<1|1,x,mid+1,r));
        }
    };
    struct SegmentTree2D
    {
        int lson[N*450],rson[N*450],pw[N*450];
        int rt[N<<2],tot;
        void build()
        {
            memset(rt,0,sizeof(rt));
            tot=0;
        }
        void create(int &k)
        {
            k=++tot;
            lson[k]=rson[k]=0;
            pw[k]=1;
        }
        void update(int &k,int L,int R,int val,int l,int r)
        {
            if(!k)create(k);
            if(L<=l&&r<=R)
            {
                pw[k]=merge(pw[k],val);
                return;
            }
            int mid=(l+r)>>1;
            if(L<=mid)update(lson[k],L,R,val,l,mid);
            if(R>mid)update(rson[k],L,R,val,mid+1,r);
        }
        int query(int k,int x,int l,int r)
        {
            if(!k)return 1;
            if(l==r)return pw[k];
            int mid=(l+r)>>1;
            if(x<=mid)return merge(pw[k],query(lson[k],x,l,mid));
            else return merge(pw[k],query(rson[k],x,mid+1,r));
        }
        void Update(int k,int U,int D,int L,int R,int val,int u,int d,int l,int r)
        {
            if(U<=u&&d<=D)
            {
                update(rt[k],L,R,val,l,r);
                return;
            }
            int mid=(u+d)>>1;
            if(U<=mid)Update(k<<1,U,D,L,R,val,u,mid,l,r);
            if(D>mid)Update(k<<1|1,U,D,L,R,val,mid+1,d,l,r);
        }
        int Query(int k,int x,int y,int u,int d,int l,int r)
        {
            if(u==d)return query(rt[k],y,l,r);
            int mid=(u+d)>>1;
            if(x<=mid)return merge(query(rt[k],y,l,r),Query(k<<1,x,y,u,mid,l,r));
            else return merge(query(rt[k],y,l,r),Query(k<<1|1,x,y,mid+1,d,l,r));
        }
    };
    SegmentTree ST;
    SegmentTree2D ST2;
    void Solve()
    {
        ST.build(1,1,n);
        ST2.build();
        FOR(i,1,m)
        {
            if(op[i]==1)
            {
                int len=qr[i]-ql[i]+1;
                ST.update(1,ql[i],qr[i],inv(len),1,n);
                if(ql[i]>1)ST.update(1,1,ql[i]-1,0,1,n);
                if(qr[i]<n)ST.update(1,qr[i]+1,n,0,1,n);
                ST2.Update(1,ql[i],qr[i],ql[i],qr[i],1ll*(len-2)*inv(len)%P,1,n,1,n);
                if(ql[i]>1)ST2.Update(1,1,ql[i]-1,ql[i],qr[i],1ll*(len-1)*inv(len)%P,1,n,1,n);
                if(qr[i]<n)ST2.Update(1,ql[i],qr[i],qr[i]+1,n,1ll*(len-1)*inv(len)%P,1,n,1,n);
            }
            else if(op[i]==2)
            {
                if(ql[i]==1)printf("%d\n",ST.query(1,qr[i],1,n));
                else printf("%d\n",ST2.Query(1,ql[i]-1,qr[i],1,n,1,n));
            }
        }
    }
};

int main()
{
    scanf("%d%d",&n,&m);
    FOR(i,1,m)scanf("%d%d%d",&op[i],&ql[i],&qr[i]);
    if(n<=3000&&m<=3000)
    {
        Subtask1::Solve();
        return 0;
    }
    Subtask2::Solve();
    return 0;
}

ZJOI 2017 樹狀數組(線段樹套線段樹)