[BZOJ]2759 一个动态树好题 LCT + 数学 + 好题[真]

来源:互联网 发布:淘宝猜你喜欢位置在哪 编辑:程序博客网 时间:2024/06/06 16:42

2759: 一个动态树好题

Time Limit: 10 Sec Memory Limit: 128 MB
Submit: 820 Solved: 278
[Submit][Status][Discuss]
Description

有N个未知数x[1..n]和N个等式组成的同余方程组:
x[i]=k[i]*x[p[i]]+b[i] mod 10007
其中,k[i],b[i],x[i]∈[0,10007)∩Z
你要应付Q个事务,每个是两种情况之一:
一.询问当前x[a]的解
A a
无解输出-1
x[a]有多解输出-2
否则输出x[a]
二.修改一个等式
C a k[a] p[a] b[a]

Input

N
下面N行,每行三个整数k[i] p[i] b[i]
Q
下面Q行,每行一个事务,格式见题目描述

Output

对每个询问,输出一行一个整数。
对100%的数据,1≤N≤30000,0≤Q≤100000,时限2秒,其中询问事务约占总数的80%

Sample Input

5

2 2 1

2 3 2

2 4 3

2 5 4

2 3 5

5

A 1

A 2

C 5 3 1 1

A 4

A 5

Sample Output

4276

7141

4256

2126

HINT

Source

By 范浩强

[Submit][Status][Discuss]

HOME Back

题解

这道题是真的动态树好题… 突然想起某道绝世好题[假] —- 其实是绝世水题.
需要对LCT维护方式很清晰才能来捉此题… 一开始一直T后来才发现vis开的bool数组… 样例太水.

首先对于每个同余方程的x将对应的p作为fa. 这样由于有n个点, 每个点都有fa, 所以一定是基环外向树森林… 基环外向树我的浅显理解就是一棵树上有一个环. 一个点x记录fa之后, 方程就变成了:
x = k * fa + b (mod m)
我们考虑怎么求解同余方程. 显然当一个联通块里的环里的一个的方程解出来, 整个联通块都解出来了. 由于有修改操作这样的动态维护, 所以想到用数据结构. 由于是基环外向树, 所以又想到拆一条边变成树, 用LCT来维护树. 砍了一条边怎么办 ? 定义每个基环外向树有个根, 根与根的fa不连边, 记一个sp[root]表示根的特殊fa. 这样将sp[root] access之后, 就能得到n个同余方程, 并且每个方程的另一个未知数(p)存在于这n个同余方程之中, 这样必定有解, 就能够解出一个解, 如果就想解出sp[root]是多少的话, 那么说明LCT需要维护最深的那个点的解.
那么考虑怎么维护: 一个x和他的fa的方程分别为
x = f * k1 + b1
f = ff * k2 + b2
合并成一个fa之后即为 x = k1 * (ff * k2 + b2) + b1, 这是一个关于解出x的方程. 记splay里每个点子树合并的最终方程为sum[x].这样splay里就可以update了, 但是必须要求是x和他的f才能合并. 这里有一个神奇的技巧, 那就是从下往上update的时候, 先合并sum[ls]和sum[x] (先后顺序有影响),再合并sum[x] 和 sum[rs[x]], 这样的合并顺序就能保证每次合并合法, 并且保存的是解出最深的那个点的解.
这样access sp[root]后, n个互相关联的同余方程合并最后会长成这个样子.
x(sp[root]) = k * x + b;求个逆元即可求出.

修改分类讨论:;

如果要改的点是一个root,那么直接修改sp就行了,否则我们要看要修改点是否在环
上,若在环上需要删边后先将root指向sp的边加进去,然后看是否会出现新环,有就讲新
父亲作为sp,否则直接连虚边。

具体见代码.
Tips:
1. 任何对一个点的访问或操作, 都要先access并splay.
2.由于根保存了信息, 所以根是固定的, 不能makeroot.

#include<bits/stdc++.h>#define mod 10007#define Boc register char#define Acce register intusing namespace std;const int maxn = 4e4 + 5;char ss[4];int n, q, idx;int fa[maxn], c[maxn][2], inv[maxn], sp[maxn], vis[maxn];inline const int read(){    Acce x = 0, f = 1;    Boc ch = getchar();    while (ch < '0' || ch > '9') {if(ch == '-')f = -1;ch = getchar();}    while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();    return f * x; }struct node{    int k, b;    node(){}    node(int k, int b) : k(k), b(b) {}    friend node operator + (const node &x, const node &y)    {   return node(x.k * y.k % mod, (y.k * x.b % mod + y.b) % mod); }}a[maxn], sum[maxn];void dfs(int x){    vis[x] = idx;    if (vis[fa[x]] == idx)    {        sp[x] = fa[x];        fa[x] = 0;    }   else if (! vis[fa[x]])            dfs(fa[x]);}inline void init(){    inv[1] = 1;    for (int i = 2; i < mod; ++ i)        inv[i] = (mod - mod / i) * inv[mod % i] % mod;}inline bool isroot(int x){   return (c[fa[x]][0] != x && c[fa[x]][1] != x) || ! fa[x];   }inline void update(int x){    sum[x] = a[x];    if (c[x][0]) sum[x] = sum[c[x][0]] + a[x];    if (c[x][1]) sum[x] = sum[x] + sum[c[x][1]];}inline void rotate(int x){    int y = fa[x], z = fa[y];    int l = (c[y][0] != x), r = l ^ 1;    if (! isroot(y)) c[z][c[z][0] != y] = x;    fa[x] = z, fa[y] = x, fa[c[x][r]] = y;    c[y][l] = c[x][r], c[x][r] = y;    update(y), update(x);}inline void splay(int x){    for (int f; ! isroot(x); rotate(x))        if (! isroot((f = fa[x])))            rotate((c[fa[f]][0] == f ^ c[f][0] == x) ? x : f);  }inline void access(int x){    for (int t = 0; x; x = fa[x])        splay(x), c[x][1] = t, update(x), t = x;}inline void cut(int x){    access(x), splay(x);    c[x][0] = fa[c[x][0]] = 0;    update(x);}inline int findroot(int x) {    access(x), splay(x);    while (c[x][0]) x = c[x][0];    splay(x);    return x;}inline bool incircle(int x, int y){    access(sp[y]), splay(sp[y]);    splay(x);    return x == sp[y] || ! isroot(sp[y]);}int main(){    init();    n = read();    for (Acce i = 1; i <= n; ++ i)        a[i].k = read(), fa[i] = read(), a[i].b = read();    for (Acce i = 1; i <= n; ++ i)        if(!vis[i])            ++ idx, dfs(i);    q = read();    int x, k, p, b, t;    for (Acce i = 1; i <= q; ++ i)    {        scanf("%s", ss);        if (ss[0] == 'A')        {            scanf("%d", &x);            access(x), splay(x);            node t1 = sum[x];            x = findroot(x);            access(sp[x]), splay(sp[x]);            node t2 = sum[sp[x]];            if(t2.k == 1)            (t2.b) ? puts("-1") : puts("-2");            else            {                int ni = inv[(1 - t2.k + mod) % mod] * t2.b % mod;                printf("%d\n", (t1.k * ni % mod + t1.b) % mod);             }        }        else        {            x = read(), k = read(), p = read(), b = read();            access(x), splay(x);            a[x] = node(k, b), update(x);            t = findroot(x);            if(t == x)            {                if (findroot(p) == t) sp[t] = p;                else sp[t] = 0, fa[t] = p; //link            } else            {                if (incircle(x, t))                {                    cut(x);                    access(t), splay(t);                    fa[t] = sp[t], sp[t] = 0;                     if (findroot(p) == x) sp[x] = p;                    else fa[x] = p;                 }   else                {                    cut(x);                    if (findroot(p) == x) sp[x] = p;                    else fa[x] = p;                 }            }        }    }}