BZOJ2759 一个动态树好题 LCT

题解:

的确是动态树好题

首先由于每个点只有一个出边

这个图构成了基环内向树

我们观察那个同余方程组

一旦形成环的话我们就能知道环上点以及能连向环上点的值是多少了

所以我们只需要用一种结构来维护两个不是直接相连点的状态

由于有删边连边操作,比较容易想到lct

我们按照套路将它拆掉一条边形成一颗树

因为我们现在只知道环上某一点的值,所以我们这棵树应该是不随便换根的

每次询问只需要把当前这个点access再splay一下 查询用根怎么表示就可以了

修改方程组等价于删一条边,连一条边

删的边如果不在环上 直接删

#include <bits/stdc++.h>
using namespace std;
#define rint register int
#define IL inline
#define rep(i,h,t) for(int i=h;i<=t;i++)
#define dep(i,t,h) for(int i=t;i>=h;i--)
#define ll long long
#define me(x) memset(x,0,sizeof(x))
#define mep(x,y) memcpy(x,y,sizeof(y))
#define mid ((h+t)>>1)
namespace IO{
    char ss[1<<24],*A=ss,*B=ss;
    IL char gc()
    {
        return A==B&&(B=(A=ss)+fread(ss,1,1<<24,stdin),A==B)?EOF:*A++;
    }
    template<class T> void read(T &x)
    {
        rint f=1,c; while (c=gc(),c<48||c>57) if (c=='-') f=-1; x=(c^48);
        while (c=gc(),c>47&&c<58) x=(x<<3)+(x<<1)+(c^48); x*=f; 
    }
    template<class T>void read2(T &x)
    {
        rint c;
        while (c=gc(),c!='A'&&c!='C'); x=c;
    }
    char sr[1<<24],z[20]; ll Z,C1=-1;
    template<class T>void wer(T x)
    {
        if (x<0) sr[++C1]='-',x=-x;
        while (z[++Z]=x%10+48,x/=10);
        while (sr[++C1]=z[Z],--Z);
    }
    IL void wer1()
    {
        sr[++C1]=' ';
    }
    IL void wer2()
    {
        sr[++C1]='\n';
    }
    template<class T>IL void maxa(T &x,T y) {if (x<y) x=y;}
    template<class T>IL void mina(T &x,T y) {if (x>y) x=y;} 
    template<class T>IL T MAX(T x,T y){return x>y?x:y;}
    template<class T>IL T MIN(T x,T y){return x<y?x:y;}
};
const int N=5e4;
int k[N],p[N],b[N];
const int mo=10007;
struct re{
    int a,b;
    re operator *(const re o) const
    {
        re c;
        c.a=a*o.a%mo; c.b=(o.a*b+o.b)%mo;
        return c;
    }
};
IL int fsp(int x,int y)
{
    int now=1;
    while (y)
    {
        if (y&1) now=now*x%mo;
        x=x*x%mo; y>>=1;
    }
    return now;
}
struct lct{
    int ls[N],fa[N],rs[N],sp[N],ans[N];
    re sum[N],v[N];
    IL void updata(int x)
    {
        if (ls[x]) sum[x]=sum[ls[x]]*v[x];
        else sum[x]=v[x];
        if (rs[x]) sum[x]=sum[x]*sum[rs[x]];
    }
    IL bool pd(int x)
    {
        return ls[fa[x]]==x||rs[fa[x]]==x;
    }
    void rotate(int x,int y)
    {
        int f1=fa[x];
        if (y==1)
        {
            rs[f1]=ls[x];
            if (ls[x]) fa[ls[x]]=f1;
        } else
        {
            ls[f1]=rs[x];
            if (rs[x]) fa[rs[x]]=f1;
        }
        fa[x]=fa[f1];
        if (pd(f1))
        {
            if (ls[fa[f1]]==f1) ls[fa[f1]]=x;
            else rs[fa[f1]]=x;
        }
        if (y==1) ls[x]=f1; else rs[x]=f1;
        fa[f1]=x;
        updata(f1); updata(x);
    }
    void splay(int x)
    {
    //    dfs(x);
       int f1=fa[x];
       while (pd(x))
       {
            if (!pd(f1))
            {
                if (ls[f1]==x) rotate(x,2); else rotate(x,1);
            } else
            if (ls[fa[f1]]==f1)
              if (ls[f1]==x) rotate(f1,2),rotate(x,2);
              else rotate(x,1),rotate(x,2);
            else 
           if (rs[f1]==x) rotate(f1,1),rotate(x,1);
              else rotate(x,2),rotate(x,1);
            f1=fa[x];
       }
    }
    void access(int x)
    {
        for (int y=0;x;y=x,x=fa[x])
        {
            splay(x); rs[x]=y; updata(x);
        }
    }
    int fdr(int x)
    {
        access(x); splay(x);
        while (ls[x]) x=ls[x];
        return x;
    }
    int query(int x)
    {
        access(x); splay(x);
        int ro=fdr(x);
        re kk=sum[x];
        access(sp[ro]); 
        splay(sp[ro]);
        int ans,a=sum[sp[ro]].a,b=sum[sp[ro]].b;
        if (a==1)
          if (b==0) ans=-2; else ans=-1;
        ans=(fsp((1-a),mo-2)*b%mo+mo)%mo;
        if (ans==-1) return(-1);
        if (ans==-2) return(-2);
        return ((ans*kk.a+kk.b)%mo+mo)%mo;
    }
    IL void cut(int x)
    {
        access(x); splay(x); ls[x]=fa[ls[x]]=0; updata(x);
    }
    void change(int x,int k,int p,int b)
    {
        int rt=fdr(x);
        if (x==rt)
        {
            if (fdr(p)!=x)
            {
              sp[x]=0; fa[x]=p;
            } else
            {
              sp[x]=p;
            }
        } else
        {
            access(sp[rt]); splay(sp[rt]);
            int z=x;
            while (pd(z)) z=fa[z];
            if (z==sp[rt])
            {
                cut(x);
                access(rt); splay(rt); fa[rt]=sp[rt]; sp[rt]=0;
            } else
            {
                cut(x);
            }
            if (fdr(p)!=x)
            {
                fa[x]=p;
            } else
            {
                sp[x]=p;
            }
        }
        v[x]=(re){k,b}; splay(x);
    }
    IL void link(int x,int y)
    {
        if (fdr(x)!=fdr(y))
        {
            fa[x]=y;
        } else sp[x]=y;
    }
}S;
int main()
{
    freopen("1.in","r",stdin);
    freopen("1.out","w",stdout);
    int n;
    IO::read(n);
    rep(i,1,n) 
    {
      IO::read(k[i]),IO::read(p[i]),IO::read(b[i]);
      S.v[i]=(re){k[i],b[i]};
      S.link(i,p[i]);
    }
    int q;
    IO::read(q);
    rep(i,1,q)
    {
        char c;
        IO::read2(c);
        if (c=='A')
        {
            int a;
            IO::read(a);
            IO::wer(S.query(a)); IO::wer2();
        } else
        {
            int a,b,c,d;
            IO::read(a); IO::read(b); IO::read(c); IO::read(d);
            S.change(a,b,c,d);
        }
    }
    fwrite(IO::sr,1,IO::C1+1,stdout);
    return 0;
}

 

删的边如果在环上 并且没有连上 把标记清除

删的边如果在环上 并且连上了 把这条边段删了,然后把标记边连上

posted @ 2018-12-19 21:04  尹吴潇  阅读(160)  评论(0编辑  收藏