bzoj 2759 一个动态树好题 LCT 数学

2 篇文章 0 订阅

果然是一道动态树好题。
《论LCT的正确使用方法》以及《出题人真会玩》系列

把每个点向p连一条边,那么这是一个基环树森林。
先从环上拆下来一条边并记录这条边指向的点,然后这玩意就变成了一坨有根树。
设根为x1,根指向的点为x2,那么所有点都可以用关于x2的一次函数表示。然后用x2本身的一次函数可以解出x2。
然后用LCT维护这坨有根树。
splay上每个点维护用这个点子树中最小(最靠左,原树深度最小)的点在原树中的父亲表示最大(最靠右,原树深度最大)的点的一次函数。
然后找每个点的表示只需要access这个点,然后splay根的一次函数就是这个点的表示。
对于修改父亲有一坨分类讨论,细节看代码吧。。

#include <bits/stdc++.h>
using namespace std;
#define N 31000
#define mod 10007
#define which(x) (ch[fa[x]][1]==x)
int n,Q,tim;
int fa[N],dir[N],vis[N],ch[N][2],ni[11000];
char s[4];
struct node
{
    int k,b;
    node(){}
    node(int k,int b):k(k),b(b){}
    friend node operator + (const node &r1,const node &r2)
    {return node(r1.k*r2.k%mod,(r2.k*r1.b%mod+r2.b)%mod);}
}a[N],sum[N];
void init()
{
    ni[1]=1;
    for(int i=2;i<mod;i++)
        ni[i]=(mod-mod/i)*ni[mod%i]%mod;
}
void dfs(int x)
{
    if(vis[x])return;
    vis[x]=tim;
    if(vis[fa[x]]==tim)
    {
        dir[x]=fa[x];
        fa[x]=0;
    }
    else dfs(fa[x]);
}
int isroot(int x)
{return !fa[x]||ch[fa[x]][which(x)]!=x;}
void pushup(int x)
{
    sum[x]=a[x];
    if(ch[x][0])sum[x]=sum[ch[x][0]]+a[x];
    if(ch[x][1])sum[x]=sum[x]+sum[ch[x][1]];
}
void rotate(int x)
{
    int y=fa[x],k=which(x);
    ch[y][k]=ch[x][k^1];
    ch[x][k^1]=y;
    if(!isroot(y))
        ch[fa[y]][which(y)]=x;
    fa[x]=fa[y];fa[y]=x;
    fa[ch[y][k]]=y;
    pushup(y);pushup(x);
}
void splay(int x)
{
    while(!isroot(x))
    {
        int y=fa[x];
        if(isroot(y))rotate(x);
        else
        {
            if(which(x)^which(y))
                rotate(x);
            else rotate(y);
            rotate(x);
        }
    }
}
void access(int x)
{
    int t=0;
    while(x)
    {
        splay(x);
        ch[x][1]=t;
        pushup(x);
        t=x;x=fa[x];
    }
}
int findroot(int x)
{
    access(x);splay(x);
    while(ch[x][0])x=ch[x][0];
    splay(x);
    return x;
}
int inc(int x,int y)
{
    access(dir[y]);splay(dir[y]);
    splay(x);
    return x==dir[y]||!isroot(dir[y]);
}
void cut(int x)
{
    access(x);splay(x);
    ch[x][0]=fa[ch[x][0]]=0;
    pushup(x);  
}
int main()
{
    //freopen("tt.in","r",stdin);
    init();
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d%d%d",&a[i].k,&fa[i],&a[i].b);
    for(int i=1;i<=n;i++)
        if(!vis[i])tim++,dfs(i);
    scanf("%d",&Q);
    for(int x,k,p,b;Q--;)
    {
        scanf("%s",s+1);
        if(s[1]=='A')
        {
            scanf("%d",&x);
            access(x);splay(x);
            node t1=sum[x];
            int t=findroot(x);
            access(dir[t]);splay(dir[t]);
            node t2=sum[dir[t]];
            if(t2.k==1)
            {
                if(t2.b)puts("-1");
                else puts("-2");
                continue;
            }
            int v1=ni[(1-t2.k+mod)%mod]*t2.b%mod;
            printf("%d\n",(t1.k*v1%mod+t1.b)%mod);
        }
        else 
        {
            scanf("%d%d%d%d",&x,&k,&p,&b);
            access(x);splay(x);
            a[x]=node(k,b);pushup(x);
            int t=findroot(x);
            if(t==x)
            {
                if(findroot(p)==t)dir[t]=p;
                else dir[t]=0,fa[t]=p;
            }
            else
            {
                if(inc(x,t))
                {
                    cut(x);
                    access(t);splay(t);
                    fa[t]=dir[t];dir[t]=0;
                    if(findroot(p)==x)dir[x]=p;
                    else fa[x]=p;
                }
                else 
                {
                    cut(x);
                    if(findroot(p)==x)dir[x]=p;
                    else fa[x]=p;
                }
            }
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值