传送门
数论与数据结构相结合的好题.
如
果
,
我
们
令
i
向
p
[
i
]
连
一
条
有
向
边
的
话
,
这
个
有
向
图
一
定
会
有
环
,
形
成
基
环
外
向
森
林
.
如
图
:
如果,我们令i向p[i]连一条有向边的话,这个有向图一定会有环,形成基环外向森林.如图:
如果,我们令i向p[i]连一条有向边的话,这个有向图一定会有环,形成基环外向森林.如图:
既然一定有环,那么变量就会循环,对于一个长度为
t
t
t的环,可以得到以下方程组:
{
x
1
≡
k
1
∗
x
t
+
b
1
(
mod
10007
)
x
2
≡
k
2
∗
x
1
+
b
2
(
mod
10007
)
…
…
x
t
≡
k
t
∗
x
t
−
1
+
b
t
(
mod
10007
)
\begin{cases}x_1≡k_1*x_t+b_1(\operatorname{mod} 10007) \\x_2≡k_2*x_1+b_2(\operatorname{mod} 10007)\\……\\x_t≡k_t*x_{t-1}+b_t(\operatorname{mod} 10007)\end{cases}
⎩⎪⎪⎪⎨⎪⎪⎪⎧x1≡k1∗xt+b1(mod10007)x2≡k2∗x1+b2(mod10007)……xt≡kt∗xt−1+bt(mod10007)
这个方程组怎么解,当然是整体代入啦.
{
x
1
≡
k
1
∗
x
t
+
b
1
(
mod
10007
)
x
2
≡
k
2
∗
x
1
+
b
2
≡
k
2
∗
(
k
1
∗
x
t
+
b
1
)
+
b
2
≡
k
1
∗
k
2
∗
x
t
+
b
1
∗
k
2
+
b
2
(
mod
10007
)
x
3
≡
k
3
∗
x
2
+
b
3
≡
k
3
∗
(
k
1
∗
k
2
∗
x
t
+
b
1
∗
k
2
+
b
2
)
+
b
3
≡
k
1
∗
k
2
∗
k
3
∗
x
t
+
b
1
∗
k
2
∗
k
3
+
b
2
∗
k
3
+
b
3
(
mod
10007
)
…
…
x
t
≡
k
t
∗
x
t
−
1
+
b
t
≡
(
∏
i
=
1
t
k
i
)
+
∑
i
=
1
t
(
b
i
∗
∏
j
=
i
+
1
t
k
j
)
(
mod
10007
)
(
数
学
归
纳
法
)
\begin{cases}x_1≡k_1*x_t+b_1(\operatorname{mod} 10007) \\x_2≡k_2*x_1+b_2≡k_2*(k_1*x_t+b_1)+b2≡k_1*k_2*x_t+b_1*k_2+b_2(\operatorname{mod} 10007)\\x_3≡k_3*x_2+b_3≡k_3*(k_1*k_2*x_t+b_1*k_2+b_2)+b_3≡k_1*k_2*k_3*x_t+b_1*k_2*k_3+b_2*k_3+b_3(\operatorname{mod} 10007)\\……\\x_t≡k_t*x_{t-1}+b_t≡(\prod_{i=1}^t k_i)+\sum_{i=1}^t(b_i*\prod_{j=i+1}^t k_j)(\operatorname{mod} 10007)(数学归纳法)\end{cases}
⎩⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎧x1≡k1∗xt+b1(mod10007)x2≡k2∗x1+b2≡k2∗(k1∗xt+b1)+b2≡k1∗k2∗xt+b1∗k2+b2(mod10007)x3≡k3∗x2+b3≡k3∗(k1∗k2∗xt+b1∗k2+b2)+b3≡k1∗k2∗k3∗xt+b1∗k2∗k3+b2∗k3+b3(mod10007)……xt≡kt∗xt−1+bt≡(∏i=1tki)+∑i=1t(bi∗∏j=i+1tkj)(mod10007)(数学归纳法)
这样,只要我们求出
x
t
x_t
xt,那么整个环及连进来的点的值都可以求出.(由此看出,每个点都要保存两个系数
k
,
b
k,b
k,b)
关于求 x t ( 以 下 简 写 为 x ) x_t(以下简写为x) xt(以下简写为x)的细节:设 x ≡ k x + b ( mod 10007 ) x≡kx+b(\operatorname{mod} 10007) x≡kx+b(mod10007)
若 k = 0 , x = b k=0,x=b k=0,x=b
若 k = 1 k=1 k=1,当 b = 0 b=0 b=0时有无穷组解;当 b ≠ 0 时 b\ne 0时 b=0时,无解.
否则, ( k − 1 ) x ≡ − b ( mod 10007 ) (k-1)x≡-b(\operatorname{mod} 10007) (k−1)x≡−b(mod10007)
我们可以用两种方法:
- e x g c d ( k − 1 , 10007 , x , y ) exgcd(k-1,10007,x,y) exgcd(k−1,10007,x,y)
- x = ( p o w ( k − 1 , 10005 ) ∗ ( 10007 − b ) mod 10007 ) x=(pow(k-1,10005)*(10007-b)\operatorname{mod} 10007) x=(pow(k−1,10005)∗(10007−b)mod10007)(先求乘法逆元)
void exgcd(int a,int b,int &x,int &y)
{
if(!b)x=1,y=0;
else exgcd(b,a%b,y,x),y-=(a/b)*x;
}
因为边会变,所以可以想到用动态树.
但是有环要怎么处理呢,我们可以把一条边拆掉,并用splay节点中的一个变量
s
f
(
s
p
e
c
i
a
l
f
a
t
h
e
r
)
sf(special ~~ father)
sf(special father)记录指向.(这条边的另一个点即为有向树的根)(详见代码中的dfs函数)
这样每个点
i
i
i都要维护两个系数
k
、
b
k、b
k、b,意义为:
x
i
=
k
∗
x
r
o
o
t
.
s
f
∗
b
x_i=k*x_{root.sf}*b
xi=k∗xroot.sf∗b.
在旋转等操作中需要维护这两个系数.
最后,提供一个小小的优化:先预处理出
[
0
,
10007
)
[0,10007)
[0,10007)的乘法逆元(毕竟询问多)
设
m
o
d
=
10007
mod=10007
mod=10007
如果用快速幂的话,预处理复杂度为
O
(
m
o
d
∗
l
o
g
m
o
d
)
O(mod*log_{mod})
O(mod∗logmod).
下面提供一个复杂度为
O
(
m
o
d
)
O(mod)
O(mod)的预处理方法:
inv[1]=1;
for(int i=2;i<mod;i++)inv[i]=(mod-mod/i)*inv[mod%i]%mod;
为什么?证明:
设 M = 10007 , t = ⌊ M / i ⌋ ; k = M mod i ; 设M=10007,t=\left\lfloor{M/i}\right\rfloor;k=M \operatorname{mod} i; 设M=10007,t=⌊M/i⌋;k=Mmodi;
t ∗ i + k ≡ 0 ( mod M ) t*i+k≡0( \operatorname{mod}M) t∗i+k≡0(modM)
− t ∗ i ≡ k ( mod M ) -t*i≡k( \operatorname{mod}M) −t∗i≡k(modM)
− t ∗ i n v [ k ] ≡ i n v [ i ] ( mod M ) -t*inv[k]≡inv[i]( \operatorname{mod}M) −t∗inv[k]≡inv[i](modM)
i n v [ i ] ≡ ( M − ⌊ M / i ⌋ ) ∗ i n v [ M mod i ] ( mod M ) inv[i]≡(M-\left\lfloor{M/i}\right\rfloor)*inv[M \operatorname{mod}i]( \operatorname{mod}M) inv[i]≡(M−⌊M/i⌋)∗inv[Mmodi](modM)
#include<cmath>
#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
#define g getchar()
#define lc tr[x].son[0]
#define rc tr[x].son[1]
using namespace std;
const int mod=10007,N=30010;
void qr(int &x)
{
char c=g;bool v=x=0;
while(!(isdigit(c)||c=='-'))c=g;
if(c=='-')v=1,c=g;
while(isdigit(c))x=x*10+c-'0',c=g;
if(v)x=-x;
}
void write(int x)
{
if(x/10)write(x/10);
putchar(x%10+'0');
}
void pri(int x)
{
if(x<0)putchar('-'),x=-x;
write(x);puts("");
}
struct data//我们把环拆成树,去掉一条边(用sf记录这条删掉的边)
{
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)
{//每个点都用sf的x表出自己,由于sf是环中深度最大的点,所以把sf吊起,他就会自己表出自己,进而求出x,表出整个环的值
data c;
c.k=k*a.k%mod;
c.b=(a.k*b+a.b)%mod;
return c;
}//x1=k1*xt+b1,x2=k2*x1+b2=k2*(k1*xt+b1)+b2=k1*k2*xt+b1*k2+b2
}d[N],s[N];
struct node{int f,son[2],sf;}tr[N];
int inv[mod];//逆元
bool v[N],in[N];//是否遍历过,是否入栈
void dfs(int x)
{
in[x]=v[x]=1;
int f=tr[x].f;
if(in[f])
{
tr[x].f=0;
tr[x].sf=f;
}
if(!v[f])dfs(f);
in[x]=0;
}
void update(int x){s[x]=s[lc]+d[x]+s[rc];}
bool rt(int x){return tr[tr[x].f].son[0]!=x&&tr[tr[x].f].son[1]!=x;}
void rotate(int x,int w)
{
int f=tr[x].f,ff=tr[f].f,r,R;
r=tr[x].son[w];R=f;tr[R].son[1-w]=r;if(r)tr[r].f=R;
r=x;R=ff;if(tr[R].son[0]==f)tr[R].son[0]=r;else if(tr[R].son[1]==f)tr[R].son[1]=r;tr[r].f=R;
r=f;R=x;tr[R].son[w]=r;tr[r].f=R;update(f);update(x);
}
void splay(int x)
{
while(!rt(x))
{
int f=tr[x].f;
if(rt(f))rotate(x,tr[f].son[0]==x);
else
{
int ff=tr[f].f,a=(tr[f].son[0]==x),b=(tr[ff].son[0]==f);
rotate(a^b?x:f,a);rotate(x,b);
}
}
}
void access(int x){for(int y=0;x;x=tr[y=x].f)splay(x),rc=y,update(x);}
int find_root(int x)//不能makeroot,因为要保证sf为环中深度最大的点
{
access(x);splay(x);
while(lc)x=lc;
return x;
}
void link(int x,int y){access(x);splay(x);tr[x].f=y;}
void cut(int x)
{
access(x);splay(x);
tr[lc].f=0;lc=0;
update(x);
}
int query(int x)
{
access(x);splay(x);
data s1=s[x];
int tx=find_root(x),f=tr[tx].sf;
access(f);splay(f);
data s2=s[f];//x=kx+b
if(!s2.k)return s1.sol(s2.b);//x=b
if(s2.k==1)return s2.b?-1:-2;//x=x+1(无解),x=x(无穷解)
return s1.sol((mod-inv[s2.k-1])*s2.b%mod);
}
bool check(int x,int tx)//判断x是否在环上
{
int y=tr[tx].sf;
if(x==y)return 1;
access(y);splay(y);splay(x);
return !rt(y);//如果y的位置变了,说明x在环上
}
void change(int x,int y,int k,int b)
{
access(x);splay(x);d[x]=(data){k,b};update(x);
int tx=find_root(x),ty;
if(x==tx)
{
ty=find_root(y);
if(ty==x)tr[x].sf=y;//x,y已经联通了,此时在加边就成环了
else tr[x].sf=0,link(x,y);//断开原来的树上没有的边,再连边。
}
else
{
//在环上
if(check(x,tx))cut(x),link(tx,tr[tx].sf),tr[tx].sf=0;//环断成树了
else cut(x);//cut完,x已经成了深度最小的
ty=find_root(y);
if(ty==x)tr[x].sf=y;
else link(x,y);
}
}
int main()
{
int n;qr(n);
inv[1]=1;
for(int i=2;i<mod;i++)inv[i]=(mod-mod/i)*inv[mod%i]%mod;/*
t=M/i;k=M%i;
t*i+k≡0(mod M)
-t*i≡k(mod M)
-t*inv[k]≡inv[i](mod M)
inv[i]≡(M-M/i)*inv[M%i](mod M)
*/
for(int i=1;i<=n;i++)
{
int k,b;qr(k);qr(tr[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",s);qr(x);
switch(s[0]){
case 'A':pri(query(x));break;
case 'C':qr(k);qr(f);qr(b);
change(x,f,k,b);break;
}
}
return 0;
}