也是水上一段时间了。
新的科研项目到手了呢~
洛谷P1501 [国家集训队] Tree II
一、题目大意
一棵 \(n\) 个点的树,每个点的初始权值为 \(1\)。
对于这棵树有 \(q\) 个操作,每个操作为以下四种操作之一:
+ u v c
:将 \(u\) 到 \(v\) 的路径上的点的权值都加上自然数 \(c\);- u1 v1 u2 v2
:将树中原有的边 \((u_1,v_1)\) 删除,加入一条新边 \((u_2,v_2)\),保证操作完之后仍然是一棵树;* u v c
:将 \(u\) 到 \(v\) 的路径上的点的权值都乘上自然数 \(c\);/ u v
:询问 \(u\) 到 \(v\) 的路径上的点的权值和,将答案对 \(51061\) 取模。
第一行两个整数 \(n,q\)。
接下来 \(n-1\) 行每行两个正整数 \(u,v\),描述这棵树的每条边。
接下来 \(q\) 行,每行描述一个操作。
对于每个询问操作,输出一行一个整数表示答案。
样例输入
3 2
1 2
2 3
* 1 3 4
/ 1 1
样例输出
4
【数据范围】
对于 \(10\%\) 的数据,\(1\le n,q \le 2000\);
另有 \(15\%\) 的数据,\(1 \le n,q \le 5\times 10^4\),没有 -
操作,并且初始树为一条链;
另有 \(35\%\) 的数据,\(1 \le n,q \le 5\times 10^4\),没有 -
操作;
对于 \(100\%\) 的数据,\(1\le n,q \le 10^5\),\(0\le c \le 10^4\)。
二、一波分析
首先有一条链的操作,那么直接锁定LCT或树链剖分。
再因为有加边、删边,直接锁定LCT。
那么,加边与删边都很简单,路径查询也是基操。
路径修改便是这道题的重点,相信读到这里的人都一定会线段树区间修改吧,这里一模一样。
完了。
一些查错:
- 一定要开unsigned int 或者 long long
- struct初始化时不能直接在结构体内部赋值,具体见代码。(如果样例516就是这个问题啦~
三、代码
#include<cstdio>
namespace Fread { const int SIZE = (1 << 18); char buf[SIZE], * p1 = buf, * p2 = buf; inline char getchar() { return (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, SIZE, stdin), p1 == p2) ? EOF : *p1++); } }
namespace Fwrite { const int SIZE = (1 << 18); char buf[SIZE], * S = buf, * T = buf + SIZE; inline void flush() { fwrite(buf, 1, S - buf, stdout), S = buf; } struct NTR { ~NTR() { flush(); } }ztr; inline void putchar(char c) { *S++ = c; if (S == T) flush(); } }
namespace Fastio {struct Reader { template <typename T> Reader& operator >> (T& x) { char c = Fread::getchar(); bool f = false; while (c < '0' or c > '9') { if (c == '-') f = true; c = Fread::getchar(); } x = 0; while (c >= '0' and c <= '9') { x = (x << 1) + (x << 3) + (c ^ 48); c = Fread::getchar(); } if (f) x = -x; return *this; }Reader& operator>>(char& c) { c = Fread::getchar(); while (c == '\n' || c == ' ' || c == '\r')c = Fread::getchar(); return *this; }Reader& operator>>(char* str) { int len = 0; char c = Fread::getchar(); while (c == '\n' || c == ' ' || c == '\r')c = Fread::getchar(); while (c != '\n' && c != ' ' && c != '\r')str[len++] = c, c = Fread::getchar(); str[len] = '\0'; return *this; }Reader() {} }cin;struct Writer { template <typename T> Writer& operator << (T x) { if (x == 0) return Fwrite::putchar('0'), * this; if (x < 0) Fwrite::putchar('-'), x = -x; static int sta[45], top = 0; while (x) sta[++top] = x % 10, x /= 10; while (top) Fwrite::putchar(sta[top] + '0'), --top; return *this; } Writer& operator<<(char c) { Fwrite::putchar(c); return*this; }Writer& operator<<(const char* str) { int cur = 0; while (str[cur])Fwrite::putchar(str[cur++]); return *this; }Writer() {} }cout;
}
#define cin Fastio :: cin
#define cout Fastio :: cout
#define int unsigned int
using namespace std;
const int N = 1e5+100,MOD = 51061;
struct node{int fa,son[2],val,sum,lazy,lazyp,lazym,siz;inline void init() {val = 1,sum = 1,lazym = 1,siz = 1;}
}T[N];
inline void swap(int &x,int &y){int tmp = x;x = y,y = tmp;
}
inline bool Isroot(int u){int f = T[u].fa;return T[f].son[0]!=u && T[f].son[1]!=u;
}
inline void Pushup(int u){T[u].sum = (T[u].val + T[T[u].son[0]].sum + T[T[u].son[1]].sum)%MOD;T[u].siz = T[T[u].son[0]].siz + T[T[u].son[1]].siz + 1;
}
inline void Reverse(int u){if(!u) return;swap(T[u].son[0],T[u].son[1]);T[u].lazy ^= 1;
}
inline void Pushdown(int u){if(T[u].lazym!=1){T[T[u].son[0]].lazym = T[T[u].son[0]].lazym*T[u].lazym%MOD;T[T[u].son[1]].lazym = T[T[u].son[1]].lazym*T[u].lazym%MOD;T[T[u].son[0]].sum = T[T[u].son[0]].sum*T[u].lazym%MOD;T[T[u].son[1]].sum = T[T[u].son[1]].sum*T[u].lazym%MOD;T[T[u].son[0]].lazyp = T[T[u].son[0]].lazyp*T[u].lazym%MOD;T[T[u].son[1]].lazyp = T[T[u].son[1]].lazyp*T[u].lazym%MOD;T[T[u].son[0]].val = T[T[u].son[0]].val*T[u].lazym%MOD;T[T[u].son[1]].val = T[T[u].son[1]].val*T[u].lazym%MOD;T[u].lazym = 1;}if(T[u].lazyp){T[T[u].son[0]].lazyp = (T[T[u].son[0]].lazyp + T[u].lazyp)%MOD;T[T[u].son[1]].lazyp = (T[T[u].son[1]].lazyp + T[u].lazyp)%MOD;T[T[u].son[0]].sum = (T[T[u].son[0]].sum + T[T[u].son[0]].siz*T[u].lazyp)%MOD;T[T[u].son[1]].sum = (T[T[u].son[1]].sum + T[T[u].son[1]].siz*T[u].lazyp)%MOD;T[T[u].son[0]].val = (T[T[u].son[0]].val + T[u].lazyp)%MOD;T[T[u].son[1]].val = (T[T[u].son[1]].val + T[u].lazyp)%MOD;T[u].lazyp = 0;}if(T[u].lazy){Reverse(T[u].son[0]),Reverse(T[u].son[1]);T[u].lazy = 0;}
}
inline void Push(int u){if(!Isroot(u)) Push(T[u].fa);Pushdown(u);
}
inline void Rotate(int u){int f = T[u].fa,g = T[T[u].fa].fa;int lorr = (T[f].son[1] == u);if(!Isroot(f)) T[g].son[T[g].son[1]==f] = u;T[u].fa = g;T[f].son[lorr] = T[u].son[lorr^1];if(T[u].son[lorr^1]) T[T[u].son[lorr^1]].fa = f;T[u].son[lorr^1] = f;T[f].fa = u;Pushup(f);
}
inline void Splay(int u){Push(u);while(!Isroot(u)){int f = T[u].fa,g = T[T[u].fa].fa;if(!Isroot(f)) Rotate(((T[f].son[1]==u)==(T[g].son[1]==f))?f:u);Rotate(u);}Pushup(u);
}
inline void Access(int u){int child = 0;while(u){Splay(u);T[u].son[1] = child;Pushup(u);child = u,u = T[u].fa;}
}
inline void Makeroot(int u){Access(u),Splay(u),Reverse(u);
}
inline void Split(int u,int v){Makeroot(u),Access(v),Splay(v);
}
inline void Link(int u,int v){Makeroot(u),T[u].fa = v;
}
inline void Cut(int u,int v){Split(u,v);if(T[v].son[0]!=u or T[v].son[1]) return;T[u].fa = T[v].son[0] = 0;Pushup(u);
}
inline int Findroot(int u){Access(u),Splay(u);while(T[u].son[0]) Pushdown(u),u = T[u].son[0];return u;
}
int n,m,x,y,c,xx,yy;
char op;
signed main(){cin>>n>>m;for(int i=1;i<=n;i++) T[i].init();for(int i=1;i<n;i++){int x,y;cin>>x>>y;Link(x,y);}for(int i=1;i<=m;i++){cin>>op;if(op=='+'){cin>>x>>y>>c;Split(x,y);T[y].sum = (T[y].sum + T[y].siz*c)%MOD;T[y].lazyp = (T[y].lazyp + c)%MOD;T[y].val = (T[y].val + c)%MOD;}else if(op=='-'){cin>>x>>y>>xx>>yy;Cut(x,y),Link(xx,yy);}else if(op=='*'){cin>>x>>y>>c;Split(x,y);T[y].lazym = T[y].lazym*c%MOD;T[y].lazyp = T[y].lazyp*c%MOD;T[y].sum = T[y].sum*c%MOD;T[y].val = T[y].val*c%MOD;}else if(op=='/'){cin>>x>>y;Split(x,y);cout<<T[y].sum<<'\n';}}return 0;
}