LuoguP1501 [国家集训队]Tree II

这题细节巨多!!!!!!

当同时维护乘法、加法标记时,根据运算先后,要先下放乘法标记。

给一个节点×v:本节点、子树和、乘法标记、加法标记均要×v。

然而改完之后还是只有5分。

56061 × 56061 = 2607225721 > 2147483647

于是int就被替换为unsigned int吧。

然后就华丽的A掉了。

我是个文明人。

#include <cstdio>
#include <algorithm>
#define N 100010
#define mod 51061
#define pa fa[x]
#define lc ch[x][0]
#define rc ch[x][1]
using namespace std;
inline char gc() {
	static char now[1<<16], *S, *T;
	if(S == T) {T = (S = now) + fread(now, 1, 1<<16, stdin); if(S == T) return EOF;}
	return *S++;
}
inline int read() {
	int x = 0; char c = gc();
	while(c < '0' || c > '9') c = gc();
	while(c >= '0' && c <= '9') {x = x * 10 + c - 48; c = gc();}
	return x;
}
namespace lct {
	int ch[N][2], fa[N], rev[N], sz[N]; unsigned int w[N], sum[N], plus[N], mult[N];
	inline void rever(int x) {swap(lc, rc); rev[x]^= 1;}
	inline void add(int x, int v) {plus[x] = (plus[x] + v) % mod; w[x] = (w[x] + v) % mod; sum[x] = (sum[x] + sz[x] * v % mod) % mod;}
	inline void tim(int x, int v) {mult[x] = mult[x] * v % mod; w[x] = w[x] * v % mod; sum[x] = sum[x] * v % mod; plus[x] = plus[x] * v % mod;}
	inline void pushdn(int x) {
		if(rev[x]) {if(lc) rever(lc); if(rc) rever(rc); rev[x] = 0;}
		if(mult[x] != 1) {if(lc) tim(lc, mult[x]); if(rc) tim(rc, mult[x]); mult[x] = 1;}
		if(plus[x]) {if(lc) add(lc, plus[x]); if(rc) add(rc, plus[x]); plus[x] = 0;}
	}
	inline int isroot(int x) {return ((ch[pa][0] != x) && (ch[pa][1] != x));}
	void pd(int x) {if(!isroot(x)) pd(pa); pushdn(x);}
	inline void update(int x) {
		sz[x] = sz[lc] + sz[rc] + 1;
		sum[x] = (sum[lc] + sum[rc] + w[x]) % mod;
	}
	inline int wh(int x) {return (ch[pa][1] == x);}
	inline void rotate(int x) {
		int y = fa[x], z = fa[y], c = wh(x);
		if(!isroot(y)) ch[z][wh(y)] = x; fa[x] = z;
		ch[y][c] = ch[x][c^1]; fa[ch[y][c]] = y;
		ch[x][c^1] = y; fa[y] = x;
		update(y); update(x);
	}
	inline void splay(int x) {
		pd(x);
		for(; !isroot(x); rotate(x))
			if(!isroot(pa)) rotate(wh(pa) == wh(x) ? pa : x);
	}
	inline void access(int x) {
		for(int y = 0; x; y = x, x = pa) splay(x), rc = y, update(x);
	}
	inline void maker(int x) {
		access(x); splay(x); rever(x);
	}
	inline void link(int x, int y) {
		maker(x); fa[x] = y;
	}
	inline void cut(int x, int y) {
		maker(x); access(y); splay(y);
		fa[x] = ch[y][0] = 0; update(y);
	}
}
int n, Q;
int main() {
	n = read(); Q = read();
	for(int i = 1; i <= n; ++i) lct::w[i] = 1, lct::sum[i] = 1, lct::mult[i] = 1, lct::sz[i] = 1;
	for(int i = 1; i < n; ++i) {int u = read(), v = read(); lct::link(u, v);}
	for(int i = 1; i <= Q; ++i) {
		char opt = '#';
		while(opt != '+' && opt != '-' && opt != '*' && opt != '/') opt = gc();
		int u = read(), v = read();
		if(opt == '+') {
			int c = read();
			lct::maker(u); lct::access(v); lct::splay(v); lct::add(v, c);
		}
		if(opt == '-') {
			lct::cut(u, v);
			u = read(); v = read();
			lct::link(u, v);
		}
		if(opt == '*') {
			int c = read();
			lct::maker(u); lct::access(v); lct::splay(v); lct::tim(v, c);
		}
		if(opt == '/') {
			lct::maker(u); lct::access(v); lct::splay(v);
			printf("%d\n", lct::sum[v]);
		}
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值