Luogu「StOI-2」簡單的樹 樹鏈剖分+線段樹+倍增

考場的時候智障了,寫了 6k+ 的樹鏈剖分.   

如果題目帶修改的話可以用樹鏈剖分來維護,但由於沒有修改用一個前綴和其實就夠了.  

求 $\sum_{i=l}^{r} f(a,i)$ 可以寫成兩個前綴相減的形式.  

然後我們就要求 $\sum_{i=0}^{r} f(a,i)$.    

求這個的話用倍增討論 $a$ 的初始值的影響範圍,因爲在影響範圍內剛開始都是由子樹中次大值來貢獻.  

然後這個次大值顯然單調,我們就可以找到貢獻會比次大值大的臨界點,貢獻是一個等差數列的形式,維護平方和以及區間和即可.   

然後對於 $a$ 的初始值貢獻不到的地方也這麼討論一下即可.   

如果加上一個帶修改還真的挺毒瘤的,不過反正考場上總共花了 60 多分鐘就過掉了. 

代碼: 

#include <cstdio>
#include <cstring>
#include <algorithm> 
#define N 500009   
#define ll long long  
#define mod 998244353   
#define lson now<<1 
#define rson now<<1|1  
#define setIO(s) freopen(s".in","r",stdin)  
using namespace std;  
ll lastans;  
int edges,n,Q,OPT,tim;  
int hd[N],to[N<<1],nex[N<<1],val[N];  
int fa[N],size[N],son[N],dfn[N],bu[N],f[20][N],dep[N],top[N]; 
void add(int u,int v) { 
    nex[++edges]=hd[u]; 
    hd[u]=edges,to[edges]=v; 
}     
int DECODE(int x) {  
    ll y=1ll*x+1ll*OPT*lastans; 
    y%=n;        
    ++y;  
    return (int)y;  
}  
struct data { 
    ll sqr,sum;  
    data() { sqr=sum=0; }
    data operator+(const data b) const { 
        data c;  
        c.sqr=sqr+b.sqr; 
        c.sum=sum+b.sum; 
        return c; 
    }
}; 
struct node { 
    data se,mx;           
    node operator+(const node b) const { 
        node c; 
        c.se=se+b.se; 
        c.mx=mx+b.mx;  
        return c;  
    }
}s[N<<2];  
struct Tree { 
    int mx,se;  
    Tree(int mx=0,int se=0):mx(mx),se(se){}      
    Tree operator+(const Tree b) const {        
        Tree c;  
        c.mx=c.se=0;  
        if(mx<b.mx) {   
            c.mx=b.mx;  
            c.se=max(mx,b.se);   
        } 
        if(mx>b.mx) {    
            c.mx=mx;  
            c.se=max(se,b.mx);  
        }     
        if(mx==b.mx) {
            c.se=c.mx=mx;  
        }
        return c;  
    }
}tree[N];  
void dfs0(int x,int ff) {   
    fa[x]=ff,size[x]=1; 
    dep[x]=dep[ff]+1;  
    f[0][x]=fa[x];  
    tree[x]=Tree(val[x],0);   
    for(int i=hd[x];i;i=nex[i]) {       
        int y=to[i]; 
        if(y==ff) continue;  
        dfs0(y,x);  
        size[x]+=size[y]; 
        if(size[y]>size[son[x]]) son[x]=y;  
        tree[x]=tree[x]+tree[y];  
    }
}
void dfs1(int x,int tp) {   
    top[x]=tp; 
    dfn[x]=++tim; 
    bu[tim]=x;   
    if(son[x]) {    
        dfs1(son[x],tp);
    } 
    for(int i=hd[x];i;i=nex[i]) { 
        int y=to[i];
        if(y==fa[x]||y==son[x]) continue;  
        dfs1(y,y);   
    }
}  
void build(int l,int r,int now) { 
    if(l==r) {  
        int cur=bu[l]; 
        s[now].mx.sum=tree[cur].mx;  
        s[now].mx.sqr=1ll*tree[cur].mx*tree[cur].mx;  
        s[now].se.sum=tree[cur].se;  
        s[now].se.sqr=1ll*tree[cur].se*tree[cur].se; 
        return; 
    }  
    int mid=(l+r)>>1;  
    build(l,mid,lson),build(mid+1,r,rson); 
    s[now]=s[lson]+s[rson];  
}    
node query(int l,int r,int now,int L,int R) {   
    if(l>=L&&r<=R) {     
        return s[now];
    } 
    int mid=(l+r)>>1;  
    if(L<=mid&&R>mid) return query(l,mid,lson,L,R)+query(mid+1,r,rson,L,R);  
    else if(L<=mid)   return query(l,mid,lson,L,R); 
    else return query(mid+1,r,rson,L,R);  
}
node Query(int x,int y) { 
    node re;  
    while(top[x]!=top[y]) {  
        if(dep[top[x]]>dep[top[y]]) {       
            re=re+query(1,n,1,dfn[top[x]],dfn[x]);  
            x=fa[top[x]]; 
        }
        else { 
            re=re+query(1,n,1,dfn[top[y]],dfn[y]); 
            y=fa[top[y]];  
        }
    }  
    if(dep[x]>dep[y]) { 
        swap(x,y); 
    }  
    re=re+query(1,n,1,dfn[x],dfn[y]); 
    return re;  
}
ll solve(int x,int r) { 
    if(r<0) return 0;  
    // 極長最大值小於等於 val[x] 的     
    int tar=x; 
    for(int i=19;i>=0;--i) {   
        if(!f[i][tar]) continue;    
        // tree[f[i][tar]].mx<=val[x] 
        if(tree[f[i][tar]].mx<=val[x]) { 
            tar=f[i][tar];  
        }
    }  
    ll ans=0;    
    if(tree[tar].mx<=val[x]) {    
        // 存在這麼一段   
        // x -> tar 這一段   
        // 先變成 0,故這一段的貢獻先是 
        int pr=x;    
        for(int i=19;i>=0;--i) { 
            if(!f[i][pr]||dep[f[i][pr]]<dep[tar]) continue;     
            if(tree[f[i][pr]].se<r) pr=f[i][pr];   
        }          
        if(tree[pr].se<r) {  
            // 等差數列求和   
            node e=Query(x,pr);  
            ans+=e.se.sum*1ll*(r+1)%mod;        // 共 r+1 個時刻    
            int num=dep[x]-dep[pr]+1;   
            ll tm=1ll*r*r*num-2ll*r*e.se.sum+1ll*r*num+e.se.sqr-e.se.sum;   
            ans+=tm/2;      
            pr=fa[pr];  
        }   // 這部分算好了        
        if(dep[pr]>=dep[tar]) { 
            // 永遠都不變的大哥     
            node e=Query(pr,tar);   
            ans+=e.se.sum*1ll*(r+1)%mod;  
            ans%=mod; 
        }     
        tar=fa[tar];  
    }    
    if(tar) {       
        // 其餘是要依靠 r 來改變的    
        node e=Query(tar,1);      
        ans+=e.mx.sum*1ll*(r+1);     
        int pr=tar;           
        for(int i=19;i>=0;--i) { 
            if(!f[i][pr]) continue;     
            if(tree[f[i][pr]].mx<r) pr=f[i][pr];   
        }     
        if(tree[pr].mx<r) {    
            e=Query(tar,pr);        
            int num=dep[tar]-dep[pr]+1;   
            ll tm=1ll*r*r*num-2ll*r*e.mx.sum+1ll*r*num+e.mx.sqr-e.mx.sum;   
            ans+=tm/2;  
            ans%=mod;  
        }
    }
    return ans%mod;   
}
char buf[100000],*p1,*p2;
#define nc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++)
int rd()
{
    int x=0; char s=nc();
    while(s<'0') s=nc();
    while(s>='0') x=(((x<<2)+x)<<1)+s-'0',s=nc();
    return x;
}  
int main() {
    /// setIO("input");     
    int x,y,z;   
    n=rd(),Q=rd(),OPT=rd();  
    for(int i=1;i<=n;++i) { 
        val[i]=rd();    
    }   
    for(int i=1;i<n;++i) { 
        x=rd(),y=rd();  
        add(x,y),add(y,x); 
    }  
    dfs0(1,0); 
    dfs1(1,1);   
    build(1,n,1); 
    for(int i=1;i<19;++i) {     
        for(int j=1;j<=n;++j) { 
            f[i][j]=f[i-1][f[i-1][j]];  
        }
    }      
    ll fin=s[1].mx.sum;  
    for(int i=1;i<=Q;++i) { 
        int l=rd(),r=rd(),a=rd();     
        l=DECODE(l),r=DECODE(r),a=DECODE(a);  
        if(l>r) { 
            swap(l,r);  
        }        
        node e=Query(1,a);    
        ll cur=(fin-1ll*e.mx.sum%mod)*(r-l+1)%mod;     
        printf("%lld\n",lastans=(ll)(cur+solve(a,r)-solve(a,l-1)+mod)%mod);  
    }  
    return 0;
}

  

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章