[BZOJ2759]一个动态树好题

题面描述

传送门

思路

思考一下不难发现应该至少有一个环(没环貌似不能求解),也就是下面这样一个同余方程组
{ x 1 ≡ k 1 ∗ x 2 + b 1 ( mod ⁡ 10007 ) x 2 ≡ k 2 ∗ x 3 + b 2 ( mod ⁡ 10007 ) x 3 ≡ k 3 ∗ x 4 + b 3 ( mod ⁡ 10007 ) ⋯ ⋯ x n ≡ k n ∗ x 1 + b n ( mod ⁡ 10007 ) \begin{cases}x_1\equiv k_1*x_2+b_1(\operatorname{mod} 10007)\\x_2\equiv k_2*x_3+b_2(\operatorname{mod} 10007)\\x_3\equiv k_3*x_4+b_3(\operatorname{mod} 10007)\\\cdots\cdots \\x_n\equiv k_n*x_1+b_n(\operatorname{mod}10007)\end{cases} x1k1x2+b1(mod10007)x2k2x3+b2(mod10007)x3k3x4+b3(mod10007)xnknx1+bn(mod10007)

其中 x 1 , x 2 , x 3 , ⋯   , x n x_1,x_2,x_3,\cdots ,x_n x1,x2,x3,,xn都可以表示成 x i ≡ ∏ k i x i + ∑ b x_i\equiv \prod k_i x_i+\sum b xikixi+b的形式,例如 x 1 ≡ ∏ i = 1 n k i x 1 + ∑ i = 1 n ( b i ∗ ∏ j = 1 i − 1 k j ) x_1\equiv \prod\limits_{i=1}^nk_i x_1+\sum\limits_{i=1}^n(b_i*\prod\limits_{j=1}^{i-1} k_j) x1i=1nkix1+i=1n(bij=1i1kj)

经过观察可以发现,如果这个环断开一条边(一个同余方程),就成了一棵树,一个环,一棵树,构成一片森林(基环外向森林)。

接着开始了 LCT ⁡ \operatorname{LCT} LCT

断开这条边的起点(父),成为原树上的根(断开之后形成的根)的 s p e c i a l   f a t h e r special\ father special father,简称 s f sf sf.

当我要把一个点调到辅助树根点,类似这样

在这里插入图片描述
那么此时

x i ≡ ∏ k i x i + ∑ b x_i\equiv \prod k_i x_i+\sum b xikixi+b其中的 ∏ k i , ∑ b \prod k_i,\sum b ki,b就可表示出来,也就是

struct data
{
    int k,b;
    data(){k=1,b=0;}
    data(int k,int b):k(k),b(b){}
    int sol(int x){return(k*x+b)%mod;}
    data operator +(const data a)const
    {
        data c;
        c.k=k*a.k%mod;
        c.b=(b*a.k%mod+a.b)%mod;
        return c;
    }/*x1=k1*x2+b1 x2=k2*x1+b2 x1=k1*k2*x1+k1*b2+b1*/
}s[N],d[N];
void update(int p)
{s[p]=s[t[p].son[0]]+d[p]+s[t[p].son[1]];}//顺序不能变
/*严格遵循规律*/

如何解决询问:

int query(int x)
{
    access(x);splay(x);
    data s1=s[x];
    int rtx=findroot(x),f=t[rtx].sf;
    access(f);splay(f);
    data s2=s[f];
    if(s2.k==1)return s2.b?-1:-2;
    if(s2.k==0)return s1.sol(s2.b);
    int X,Y;
    exgcd(s2.k-1,mod,X,Y);
    return s1.sol((mod-X)%mod*s2.b%mod); 
}

( k − 1 ) ∗ x + b i ≡ 0 ( mod ⁡ 10007 ) (k-1)*x+b_i\equiv0(\operatorname{mod}10007) (k1)x+bi0(mod10007)
( k − 1 ) ∗ x + 10007 ∗ y ≡ − b i ( mod ⁡ 10007 ) (k-1)*x+10007*y\equiv-b_i(\operatorname{mod}10007) (k1)x+10007ybi(mod10007)

打通 f − > r t x f->rtx f>rtx的路径,就可以构成环。就可以求得 x f x_f xf的解,进而求到 x x x的解。

对于 k = 1 k=1 k=1,当且仅当 b = 0 b=0 b=0有无穷解,否则无解。

对于 k = 0 k=0 k=0 x = b x=b x=b

AC code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#define gc getchar()
using namespace std;
const int mod=10007;
const int N=3e4+10;
inline void qr(int &x)
{
    x=0;int f=1;char c=gc;
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=gc;}
    while(c>='0'&&c<='9'){x=x*10+(c^48);c=gc;}
    x*=f;
}
void qw(int x)
{
    if(x<0)x=-x,putchar('-');
    if(x/10)qw(x/10);
    putchar(x%10+48);
}
struct data
{
    int k,b;
    data(){k=1,b=0;}
    data(int k,int b):k(k),b(b){}
    int sol(int x){return(k*x+b)%mod;}
    data operator +(const data a)const
    {
        data c;
        c.k=k*a.k%mod;
        c.b=(b*a.k%mod+a.b)%mod;
        return c;
    }/*x1=k1*x2+b1 x2=k2*x1+b2 x1=k1*k2*x1+k1*b2+b1*/
};
int exgcd(int a,int b,int &x,int &y)
{
    if(!b){x=1;y=0;return ;}
    else{exgcd(b,a%b,y,x);y-=(a/b)*x;}
}
struct node{int f,son[2],sf;}t[N];//sf ->special father
data d[N],s[N];
inline bool nroot(int p){return t[t[p].f].son[0]==p||t[t[p].f].son[1]==p;}  
void update(int p)
	{s[p]=s[t[p].son[0]]+d[p]+s[t[p].son[1]];}
bool v[N],ins[N];//in_stack
void dfs(int x)
{
    ins[x]=v[x]=1;
    int f=t[x].f;
    if(ins[f])//环 
    {
        t[x].f=0;
        t[x].sf=f;
    }
    if(!v[f])dfs(f);
    ins[x]=0;   
}
void rotate(int p,int w)
{
    int f=t[p].f,gf=t[f].f;
    int r=t[p].son[w],R=f;t[R].son[w^1]=r;if(r)t[r].f=R;
    r=p;R=gf;if(nroot(f))t[R].son[t[R].son[1]==f]=r;t[r].f=R;
    r=f;R=p;t[R].son[w]=r;t[r].f=R;update(f),update(p);
}
void splay(int p)
{
    while(nroot(p))
    {
        int f=t[p].f,gf=t[f].f;
        if(!nroot(f))rotate(p,t[f].son[0]==p);
        else
        {
            if(t[f].son[0]==p&&t[gf].son[0]==f)rotate(f,1),rotate(p,1);
            else if(t[f].son[0]==p&&t[gf].son[1]==f)rotate(p,1),rotate(p,0);
            else if(t[f].son[1]==p&&t[gf].son[0]==f)rotate(p,0),rotate(p,1);
            else rotate(f,0),rotate(p,0);
        }
    }
}
void access(int x)
{
    for(int y=0;x;x=t[y=x].f)
        splay(x),t[x].son[1]=y,update(x);
}
int findroot(int x)
{
    access(x);splay(x);
    while(t[x].son[0])x=t[x].son[0];
    return x;
}
void link(int x,int y)
{
    access(x);splay(x);t[x].f=y;
}
void cut(int x)
{
    access(x);splay(x);
    t[t[x].son[0]].f=0;
    t[x].son[0]=0;
    update(x);
}
int query(int x)
{
    access(x);splay(x);
    data s1=s[x];
    int rtx=findroot(x),f=t[rtx].sf;
    access(f);splay(f);
    data s2=s[f];
    if(s2.k==1)return s2.b?-1:-2;
    if(s2.k==0)return s1.sol(s2.b);
    int X,Y;
    exgcd(s2.k-1,mod,X,Y);
    return s1.sol((mod-X)%mod*s2.b%mod); 
}
bool check(int x,int rtx)//is_in_circle?
{
    int y=t[rtx].sf;
    if(x==y)return 1;
    access(y);splay(y);splay(x);
    return nroot(y);
}
void change(int x,int y,int k,int b)
{
    access(x);splay(x);d[x]=data(k,b);update(x);
    int rtx=findroot(x);
    if(x==rtx)
    {
        int rty=findroot(y);
        if(rty==x)t[x].sf=y;//in_circle
        else t[x].sf=0,link(x,y);//out 
    }
    else
    {
        if(check(x,rtx))
        {
            cut(x);link(rtx,t[rtx].sf);
            t[rtx].sf=0;
            int rty=findroot(y);
            if(rty==x)t[x].sf=y;
            else link(x,y);
        }   
        else
        {
            cut(x);
            int rty=findroot(y);
            if(rty==x)t[x].sf=y;
            else link(x,y);
        }
    }
}
int main()
{
    int n;qr(n);
    for(int i=1;i<=n;i++)
    {
        int k,b;qr(k),qr(t[i].f),qr(b);
        d[i]=s[i]=data(k,b);
    }
    for(int i=1;i<=n;i++)if(!v[i])dfs(i);//建基环森林 
    int m;qr(m);
    while(m--)
    {
        int x,k,b,f;
        char s[3];scanf("%s%d",s+1,&x);
        if(s[1]=='A')qw(query(x)),puts("");
        else
        {
            qr(k),qr(f),qr(b);
            change(x,f,k,b);
        }
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值