\(\color{#0066ff}{ 题目描述 }\)
一棵n个点的树,每个点的初始权值为1。对于这棵树有q个操作,每个操作为以下四种操作之一:
-
+ u v c
:将u到v的路径上的点的权值都加上自然数c; -
- u1 v1 u2 v2
:将树中原有的边(u1,v1)删除,加入一条新边(u2,v2),保证操作完之后仍然是一棵树; -
* u v c
:将u到v的路径上的点的权值都乘上自然数c; -
/ u v
:询问u到v的路径上的点的权值和,求出答案对于51061的余数。
\(\color{#0066ff}{输入格式}\)
第一行两个整数n,q
接下来n-1行每行两个正整数u,v,描述这棵树
接下来q行,每行描述一个操作
\(\color{#0066ff}{输出格式}\)
对于每个/对应的答案输出一行
\(\color{#0066ff}{输入样例}\)
3 2
1 2
2 3
* 1 3 4
/ 1 1
\(\color{#0066ff}{输出样例}\)
4
\(\color{#0066ff}{数据范围与提示}\)
10%的数据保证,\(1\leq n,q\leq 2000\)
另外15%的数据保证,\(1\leq n,q\leq 5*10^4\),没有-操作,并且初始树为一条链
另外35%的数据保证,\(1\leq n,q\leq 5*10^4\),没有-操作
100%的数据保证,\(1\leq n,q\leq 10^5,0\leq c\leq 10^4\)
\(\color{#0066ff}{ 题解 }\)
显然有link,cut的操作
所以用LCT来维护这个树
对于那些加,乘,类比线段树打标记来维护
#include<bits/stdc++.h>
using namespace std;
#define LL long long
LL in() {
char ch; int x = 0, f = 1;
while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
return x * f;
}
const int mod = 51061;
const int maxn = 1e5 + 5;
struct LCT {
protected:
struct node {
node *ch[2], *fa;
LL val, num, add, mul, siz, rev;
node(LL val = 1, LL num = 1, LL add = 0, LL mul = 1, LL siz = 1, int rev = 0)
: val(val), num(num), add(add), mul(mul), siz(siz), rev(rev) { ch[0] = ch[1] = fa = NULL; }
void g(LL a, LL m) {
(val *= m) %= mod, (num *= m) %= mod, (add *= m) %= mod, (mul *= m) %= mod;
(val += a) %= mod, (num += siz * a % mod) %= mod, (add += a) %= mod;
}
bool ntr() { return fa && (fa->ch[0] == this || fa->ch[1] == this); }
bool isr() { return this == fa->ch[1]; }
void trn() { std::swap(ch[0], ch[1]), rev ^= 1; }
void dwn() {
if(rev) {
if(ch[0]) ch[0]->trn();
if(ch[1]) ch[1]->trn();
rev = 0;
}
if(ch[0]) ch[0]->g(add, mul);
if(ch[1]) ch[1]->g(add, mul);
add = 0, mul = 1;
}
void upd() {
siz = 1, num = val;
if(ch[0]) siz += ch[0]->siz, num += ch[0]->num;
if(ch[1]) siz += ch[1]->siz, num += ch[1]->num;
}
}s[maxn], *t[maxn];
int top;
void rot(node *x) {
node *y = x->fa, *z = y->fa;
int k = x->isr(); node *w = x->ch[!k];
if(y->ntr()) z->ch[y->isr()] = x;
x->ch[!k] = y, y->ch[k] = w;
y->fa = x, x->fa = z;
if(w) w->fa = y;
y->upd(), x->upd();
}
void splay(node *o) {
t[top = 1] = o;
while(t[top]->ntr()) t[top + 1] = t[top]->fa, top++;
while(top) t[top--]->dwn();
while(o->ntr()) {
if(o->fa->ntr()) rot(o->isr() ^ o->fa->isr()? o : o->fa);
rot(o);
}
}
void access(node *x) {
for(node *y = NULL; x; x = (y = x)->fa)
splay(x), x->ch[1] = y, x->upd();
}
void makeroot(node *x) { access(x), splay(x), x->trn(); }
node *findroot(node *x) {
access(x), splay(x);
while(x->dwn(), x->ch[0]) x = x->ch[0];
return splay(x), x;
}
void link(node *x, node *y) {
if(findroot(x) == findroot(y)) return;
makeroot(x), x->fa = y;
}
void cut(node *x, node *y) {
makeroot(x), access(y), splay(y);
if(y->ch[0] == x) y->ch[0] = x->fa = NULL;
}
int query(node *x, node *y) {
makeroot(x), access(y), splay(y);
return y->num % mod;
}
void addpath(node *x, node *y, LL c) {
makeroot(x), access(y), splay(y);
y->g(c, 1);
}
void mulpath(node *x, node *y, LL c) {
makeroot(x), access(y), splay(y);
y->g(0, c);
}
public:
void link(int x, int y) { link(s + x, s + y); }
void cut(int x, int y) { cut(s + x, s + y); }
void add(int x, int y, LL c) { addpath(s + x, s + y, c); }
void mul(int x, int y, LL c) { mulpath(s + x, s + y, c); }
int query(int x, int y) { return query(s + x, s + y); }
}v;
char getch() {
char ch = getchar();
while(ch != '+' && ch != '-' && ch != '*' && ch != '/') ch = getchar();
return ch;
}
int main() {
int n = in(), q = in();
for(int i = 1; i < n; i++) v.link(in(), in());
int a, b, c, d;
while(q --> 0) {
char ch = getch();
if(ch == '+') a = in(), b = in(), c = in(), v.add(a, b, c);
if(ch == '-') a = in(), b = in(), c = in(), d = in(), v.cut(a, b), v.link(c, d);
if(ch == '*') a = in(), b = in(), c = in(), v.mul(a, b, c);
if(ch == '/') a = in(), b = in(), printf("%d\n", v.query(a, b));
}
return 0;
}