P1501 [国家集训队]Tree II

注意下传标记的LCTLCT`
下传方法与线段树2异斧同工,乘法优先,加法其次。

每次下传时,注意到一颗SplaySplay维护了一条链的信息,所以我只需要split(x,y)split(x,y)
把x-yx−y这一条链拉出来,然后就再在顶部(y)(y)处打上一个标记即可。

其他处理都类似的需要splitsplit
比如:

询问x-yx−y的答案,考虑先split(x,y)split(x,y),然后yy是SplaySplay的根,所以我们直接输出t[y].val(valt[y].val(val 表示这颗子树的和))
操作22是直接:cut\quad&\quad linkcut&link
乘法和加法一样,先splitsplit,然后在SplaySplay的根部打上一个标记即可。

注意细节,与线段树2较为相似
附上AC代码
#include<bits/stdc++.h>
using namespace std;
#define il inline
#define int unsigned int
il int read() {
char cc = getchar(); int cn = 0, flus = 1;
while(cc < ‘0’ || cc > ‘9’) { if( cc == ‘-’ ) flus = -flus; cc = getchar(); }
while(cc >= ‘0’ && cc <= ‘9’) cn = cn * 10 + cc - ‘0’, cc = getchar();
return cn * flus;
}
#define ls(x) t[x].son[0]
#define rs(x) t[x].son[1]
#define rep( i, s, t ) for( register int i = s; i <= t; ++ i )
#define mod 51061
const int N = 3e5 + 5;
char s[5];
struct LCT{
int son[2], val, add, mul, fa, size;
bool mark;
}t[N];
int w[N], n, q;
void push1( int x, int c ) {
t[x].val = ( t[x].size * c % mod + t[x].val ) % mod;
w[x] += c, t[x].add += c, t[x].add %= mod;
w[x] %= mod;
}
void push2( int x, int c ) {
t[x].val *= c, w[x] *= c, t[x].mul *= c, t[x].add *= c; //要给add也乘上c
t[x].val %= mod, w[x] %= mod, t[x].mul %= mod, t[x].add %= mod;
}
void pushmark( int x ) { //pushmark的部分与线段树2相同QWQ…
if( t[x].mul != 1 ) push2( ls(x), t[x].mul ), push2( rs(x), t[x].mul ), t[x].mul = 1;
if( t[x].add ) push1( ls(x), t[x].add ), push1( rs(x), t[x].add ), t[x].add = 0;
if( t[x].mark ) {
t[x].mark = 0, t[ls(x)].mark ^= 1, t[rs(x)].mark ^= 1;
swap( ls(x), rs(x) );
}
t[x].val %= mod, w[x] %= mod; //注意取模
}
void pushup( int x ) {
t[x].val = ( w[x] + t[ls(x)].val + t[rs(x)].val ) % mod,
t[x].size = t[ls(x)].size + t[rs(x)].size + 1;
t[x].val %= mod;
}
bool isroot( int x ) {
return ( rs(t[x].fa) != x ) && ( ls(t[x].fa) != x );
}
void rotate( int x ) {
int f = t[x].fa, ff = t[f].fa, qwq = ( rs(f) == x );
t[x].fa = ff;
if( !isroot(f) ) t[ff].son[(rs(ff) == f)] = x; //如果父亲不为根才改爷爷
t[t[x].son[qwq ^ 1]].fa = f, t[f].son[qwq] = t[x].son[qwq ^ 1],
t[f].fa = x, t[x].son[qwq ^ 1] = f;
pushup(f), pushup(x);
}
int st[N];
void Splay( int x ) {
int top = 0, now = x;
st[++top] = x;
while( !isroot(now) ) st[++top] = ( now = t[now].fa );
while( top ) pushmark( st[top–] );
while( !isroot(x) ) {
int f = t[x].fa, ff = t[f].fa;
if( !isroot(f) ) ( ( rs(f) == x ) ^ ( rs(ff) == f ) ) ? rotate(x) : rotate(f);
rotate(x);
}
}
void access( int x ) {
for( int y = 0; x; y = x, x = t[y].fa )
Splay( x ), t[x].son[1] = y, pushup( x );
}
void makeroot( int x ) {
access( x ), Splay( x ), t[x].mark ^= 1, pushmark( x );
}
int findroot( int x ) {
access( x ), Splay( x ), pushmark( x );
while( ls(x) ) pushmark( x = ls(x) );
return x;
}
void split( int x, int y ) { //split
makeroot( x ), access( y ), Splay( y );
}
void link( int x, int y ) {
makeroot( x );
if( findroot( y ) != x ) t[x].fa = y; //还是要判断…
}
void cut( int x, int y ) { //明明数据保证了合法,但实际上还是要判断…
makeroot( x );
if( findroot(y) == x && t[x].fa == y && ls(y) == x && !rs(x) )
t[x].fa = t[y].son[0] = 0, pushup( y );
}
signed main()
{
n = read(), q = read(); int x, y, u1, u2, v1, v2, c;
rep( i, 1, n - 1 ) x = read(), y = read(), link( x, y ), t[i].mul = w[i] = 1; //这里要注意初始化。
t[n].mul = w[n] = 1;
rep( i, 1, q ) {
scanf("%s", s);
if( s[0] == ‘+’ ) u1 = read(), v1 = read(), c = read() % mod, split( u1, v1 ), push1( v1, c );

    if( s[0] == '-' ) u1 = read(), v1 = read(), u2 = read(), v2 = read(), cut( u1, v1 ), link( u2, v2 );

    if( s[0] == '/' ) u1 = read(), v1 = read(), split( u1, v1 ), printf("%d\n", t[v1].val % mod);

    if( s[0] == '*')  u1 = read(), v1 = read(), c = read() % mod, split( u1, v1 ), push2( v1, c );
}
return 0;

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值