一棵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的余数。
最近又练了几道LCT的题,于是发现了这道好题……
一棵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的余数。
数据规模和约定
10%的数据保证,1<=n,q<=2000
另外15%的数据保证,1<=n,q<=5*10^4,没有-操作,并且初始树为一条链
另外35%的数据保证,1<=n,q<=5*10^4,没有-操作
100%的数据保证,1<=n,q<=10^5,0<=c<=10^4
这题相当坑爹……明明LCT的代码和一道已经AC了的题的代码是一样的,对拍还是过不了,还一度怀疑自己的LCT模板是错的……最后上网找别人的题解(百度只找到两篇),发现原来:取模的数51061^2会爆int……改成long long就能AC了……
吐槽先到这里
这题一共三类操作,+和*操作都是修改链上点权,-操作是改变树的形态,/操作是查询。具体细节可以看之前的《LCT小结》文章,这里重点讲一下标记的问题。
个人把标记分为了两种:覆盖类标记和非覆盖类标记。所谓覆盖类标记就是,这个标记对区间的影响是和这个区间的值以及已有的标记无关的,比如区间赋值或者区间翻转。其他的标记就是非覆盖类标记,比如区间加减乘除、区间取反(对于01串)之类的。
对于覆盖类标记不用想太多,如果是像区间赋值这样会“覆盖”已有的非覆盖类标记的标记,那就清除已有标记,然后直接打上新的标记;如果是区间翻转这种和其他标记独立开来的标记,就单独处理。处理这类标记时不需要先pushdown。
对于非覆盖类标记就要仔细思考了。因为标记之间有可能互相影响,所以处理标记的顺序是很重要的。就拿这道题来说,有两类非覆盖类标记:加法标记和乘法标记。应该先下传乘法标记,再下传加法标记。同时下传乘法标记时,要给儿子的加法增量也乘上乘法标记,写成公式就是(x + delta) * c = x * c + delta * c。有同样标记的线段树题有一道BZOJ1798。Splay和线段树中的标记下传类似(可以说完全相同)。
还有就是标记下传(pushdown)与信息更新(update)的时机。由于下传的时候会直接修改子节点的value和sum(如果有)等信息,也就是说,打了标记的节点本身的信息已经是最新了的,所以不要update,update反而错了。个人总结的Splay需要pushdown的地方分别是zig和zag过程中对x进行下传、splay过程中每次旋转前对x的祖父节点(如果有)和父节点依次下传,最后在splay过程结束之前先下传再更新。详见代码。
这题还有另外一个需要注意的地方,也是一个很强的常数优化,就是由于题目保证了所有数据肺腑,所以可以用unsigned int而非long long来存储。实测unsigned int比long long要快将近一倍。
代码:(目前在BZOJ和清橙上都是最快的……大概可以认为这个模板常数还是比较优的吧?)
//Tsinsen A1303; tree; Link-Cut Tree
#include
#include
#include
#define N 100000
typedef unsigned int uint;
const uint mod = 51061;
struct edge
{
uint next, node;
}e[N << 1 | 1];
uint head[N + 1], tot = 0;
uint n, q, x, y, w;
bool v[N + 1];
class Node
{
public:
Node *l, *r, *f;
uint v, s, sum, add, mul;
bool rev;
Node() {}
Node(uint _v) : s(1), add(0), mul(1), rev(false) { l = r = f = NULL, sum = v = _v; }
}*p[N + 1];
char cmd[10];
inline void addedge(uint a, uint b)
{
e[++tot].next = head[a];
head[a] = tot, e[tot].node = b;
}
inline uint add(uint a, uint b)
{ return a + b >= mod ? a + b - mod : a + b; }
inline void mul(uint &a, uint b)
{ a = (a * b) % mod; }
inline bool isRoot(Node *x)
{ return !(x->f) || (x->f->l != x && x->f->r != x); }
inline void update(Node *x)
{
x->sum = add(x->v, add((x->l ? x->l->sum : 0), (x->r ? x->r->sum : 0)));
x->s = (x->l ? x->l->s : 0) + (x->r ? x->r->s : 0) + 1;
}
inline void modifyTree(Node *x, uint a, uint m)
{
if (m != 1)
{
mul(x->mul, m);
mul(x->sum, m);
mul(x->v, m);
mul(x->add, m);
}
if (a)
{
x->add = add(x->add, a);
x->sum = add(x->sum, (x->s * a) % mod);
x->v = add(x->v, a);
}
}
inline void push(Node *x)
{
if (x->rev)
{
x->rev = false;
if (x->l) x->l->rev ^= 1;
if (x->r) x->r->rev ^= 1;
std::swap(x->l, x->r);
}
if (!(x->add) && x->mul == 1) return ;
if (x->l) modifyTree(x->l, x->add, x->mul);
if (x->r) modifyTree(x->r, x->add, x->mul);
x->add = 0, x->mul = 1;
}
inline void zig(Node *x)
{
Node *y = x->f;
Node *z = y->f;
push(x);
if (z && z->l == y) z->l = x;
else if (z && z->r == y) z->r = x;
if (x->r) x->r->f = y;
y->l = x->r;
x->r = y, y->f = x, x->f = z;
update(y);
}
inline void zag(Node *x)
{
Node *y = x->f;
Node *z = y->f;
push(x);
if (z && z->l == y) z->l = x;
else if (z && z->r == y) z->r = x;
if (x->l) x->l->f = y;
y->r = x->l;
x->l = y, y->f = x, x->f = z;
update(y);
}
inline void splay(Node *x)
{
Node *y, *z;
while (!isRoot(x))
{
y = x->f;
z = y->f;
if (z) push(z);
push(y);
if (isRoot(y))
if (y->l == x) zig(x);
else zag(x);
else
if (z->l == y)
if (y->l == x) zig(y), zig(x);
else zag(x), zig(x);
else
if (y->l == x) zig(x), zag(x);
else zag(y), zag(x);
}
push(x);
update(x);
}
inline void expose(Node *x)
{
for (Node *y = NULL; x; x = x->f)
{
splay(x);
x->r = y;
update(y = x);
}
}
inline void makeRoot(Node *x)
{
expose(x);
splay(x);
x->rev ^= 1;
}
inline void link(Node *x, Node *y)
{
makeRoot(x);
x->f = y;
}
inline void cut(Node *x, Node *y)
{
expose(y);
splay(x);
if (x->f == y) x->f = NULL;
else
{
expose(x);
splay(y);
y->f = NULL;
}
}
void dfs(uint x)
{
v[x] = true;
p[x] = new Node(1);
for (uint i = head[x]; i; i = e[i].next)
{
if (v[e[i].node]) continue;
dfs(e[i].node);
p[e[i].node]->f = p[x];
}
}
inline void modify(uint _x, uint _y, uint a, uint m)
{
Node *x = p[_x], *y = p[_y];
expose(y);
for (y = NULL; x; x = x->f)
{
splay(x);
if (!(x->f))
{
if (y) modifyTree(y, a, m);
if (x->r) modifyTree(x->r, a, m);
x->v = add(x->v, a);
mul(x->v, m);
}
x->r = y;
update(y = x);
}
}
inline uint query(uint _x, uint _y)
{
uint ret = 0;
Node *x = p[_x], *y = p[_y];
expose(y);
for (y = NULL; x; x = x->f)
{
splay(x);
if (!(x->f))
{
ret = x->v;
if (y) ret = add(ret, y->sum);
if (x->r) ret = add(ret, x->r->sum);
break;
}
x->r = y;
update(y = x);
}
return ret;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("input.txt", "r", stdin);
freopen("output.txt", "w", stdout);
#endif
scanf("%d%d", &n, &q);
for (uint i = 1; i < n; ++i)
{
scanf("%d%d", &x, &y);
addedge(x, y), addedge(y, x);
}
dfs(1);
while (q--)
{
scanf("%s%d%d", cmd, &x, &y);
if (cmd[0] == '+' || cmd[0] == '*') //Add x y w / Mul x y w
{
scanf("%d", &w);
if (cmd[0] == '+') modify(x, y, w % mod, 1);
else modify(x, y, 0, w % mod);
}
else if (cmd[0] == '-') //Cut x y Link a b
{
cut(p[x], p[y]);
scanf("%d%d", &x, &y);
link(p[x], p[y]);
}
else //Sum x y
printf("%d\n", query(x, y));
}
return 0;
}