一些LCT最基本也是最核心的操作,其他一些维护树链信息的可以直接参照树链剖分。
#include<bits/stdc++.h>
using namespace std;
namespace LCT{
struct gg{
int ch[2],fa;
bool rev;
}node[10010];
void init()
{for(int i=0;i<=10000;i++)node[i].ch[0]=node[i].ch[1]=node[i].fa=node[i].rev=0;}//毫无卵用的清空
int get(int x)//询问该点是父亲的左儿子或右儿子或都不是(都不是即该点为所在splay的根,通过轻边单向连父亲)
{
if(!node[x].fa)return -1;
if(node[node[x].fa].ch[0]==x)return 0;
if(node[node[x].fa].ch[1]==x)return 1;
return -1;
}
void push_down(int x)//下放翻转标记,同普通splay
{
if(x&&node[x].rev)
{
node[node[x].ch[0]].rev^=1;
node[node[x].ch[1]].rev^=1;
swap(node[x].ch[0],node[x].ch[1]);
node[x].rev=0;
}
}
void push(int x)//下放x点到所在splay的根的所有标记
{
if(get(x)!=-1)push(node[x].fa);
push_down(x);
}
void rotate(int x)//splay基本操作,注意判断父亲是所在splay根的情况
{
int fx=node[x].fa,gx=node[fx].fa,op=get(x),fop=get(fx);
node[fx].ch[op]=node[x].ch[op^1],node[node[x].ch[op^1]].fa=fx;
node[x].fa=gx;if(fop!=-1)node[gx].ch[fop]=x;
node[x].ch[op^1]=fx,node[fx].fa=x;
}
void splay(int x)//splay基本操作,注意先下放标记
{
push(x);
for(int fx=node[x].fa;get(x)!=-1;rotate(x),fx=node[x].fa)
{
if(get(fx)!=-1)
{
(get(x)==get(fx))?rotate(fx):rotate(x);
}
}
}
void access(int x)//最重要操作,即我在前一篇说的更改轻重链,它能将x节点与整棵树根(不是所在splay)连到一个重链里
//并且x节点和儿子都以虚边连接以支持换根
{
for(int y=0;x;)
{
splay(x);//先转到当前splay的根
node[x].ch[1]=y;//右儿子断开,变为上次转到splay的点
y=x,x=node[x].fa;//更新
}
}
void make_root(int x)//换根操作,令x成为整棵树根(基于access后x为所在splay最深点性质)
{
access(x),splay(x),node[x].rev^=1;//打标记是因为x变为根后它的深度要变为最小,因此需翻转
}
void split(int x,int y)//把x与y的路径组成一个以y为根的splay
{
make_root(x),access(y),splay(y);
}
void cut(int x,int y)//把x与y分离
{
split(x,y);
node[x].fa=node[y].ch[0]=0;
}
void line(int x,int y)//连接x,y
{
make_root(x),node[x].fa=y;
}
int find(int x)//查找x点所在树的根节点
{
for(access(x),splay(x);node[x].ch[0];x=node[x].ch[0]);
return x;
}
}
using namespace LCT;
int main()
{
int n,m,x,y;char op[20];
scanf("%d%d",&n,&m);
init();
while(m--)
{
scanf("%s",op);
scanf("%d%d",&x,&y);
if(op[0]=='Q')find(x)==find(y)?puts("Yes"):puts("No");
else if(op[0]=='C')line(x,y);
else if(op[0]=='D')cut(x,y);
}
}
upd:
维护到根路径上点权最值,优化码风(看以前的代码写成一坨跟yzy一样,真是难受)
#include<bits/stdc++.h>
#define pii pair<int,int>
#define fi first
#define sc second
#define pb push_back
#define ll long long
#define trav(v,x) for(auto v:x)
#define all(x) (x).begin(), (x).end()
#define VI vector<int>
#define VLL vector<ll>
#define pll pair<ll, ll>
#define double long double
#define int long long
using namespace std;
const int N = 1e6 + 100;
const ll inf = 1e9;
const ll mod = 998244353;//1e9 + 7;
#ifdef LOCAL
void debug_out(){cerr << endl;}
template<typename Head, typename... Tail>
void debug_out(Head H, Tail... T)
{
cerr << " " << to_string(H);
debug_out(T...);
}
#define debug(...) cerr << "[" << #__VA_ARGS__ << "]:", debug_out(__VA_ARGS__)
#else
#define debug(...) 42
#endif
namespace LCT{
#define ls ch[0]
#define rs ch[1]
int tot;
struct gg{
int ch[2], fa, las, val, mx, ps;
bool rev;
void in(int x, int y)
{
las = x, val = y, ps = tot;
ch[0] = ch[1] = fa = mx = 0;
rev = 0;
}
}node[N];
void init()
{
for(int i = 0; i <= tot; i++)
{
node[i].ch[0] = node[i].ch[1] = node[i].fa = node[i].rev = 0;
node[i].val = node[i].las = node[i].mx = 0;
}
tot = 0;
}//毫无卵用的清空
int get(int x)//询问该点是父亲的左儿子或右儿子或都不是(都不是即该点为所在splay的根,通过轻边单向连父亲)
{
if(!node[x].fa)
return -1;
if(node[node[x].fa].ch[0]==x)
return 0;
if(node[node[x].fa].ch[1]==x)
return 1;
return -1;
}
void push_up(int x)
{
node[x].mx = node[x].val, node[x].ps = x;
int l = node[x].ls, r = node[x].rs;
if(l && node[x].mx < node[l].mx)
node[x].mx = node[l].mx, node[x].ps = node[l].ps;
if(l && node[x].mx == node[l].mx && node[x].ps < node[l].ps)
node[x].mx = node[l].mx, node[x].ps = node[l].ps;
swap(l, r);
if(l && node[x].mx < node[l].mx)
node[x].mx = node[l].mx, node[x].ps = node[l].ps;
if(l && node[x].mx == node[l].mx && node[x].ps < node[l].ps)
node[x].mx = node[l].mx, node[x].ps = node[l].ps;
}
void push_down(int x)//下放翻转标记,同普通splay
{
if(x && node[x].rev)
{
node[node[x].ch[0]].rev^=1;
node[node[x].ch[1]].rev^=1;
swap(node[x].ch[0],node[x].ch[1]);
node[x].rev=0;
}
}
void push(int x)//下放x点到所在splay的根的所有标记
{
if(get(x) != -1)push(node[x].fa);
push_down(x);
}
void rotate(int x)//splay基本操作,注意判断父亲是所在splay根的情况
{
int fx = node[x].fa, gx = node[fx].fa, op = get(x), fop = get(fx);
node[fx].ch[op] = node[x].ch[op ^ 1], node[node[x].ch[op ^ 1]].fa = fx;
node[x].fa = gx;
if(fop!=-1)
node[gx].ch[fop] = x;
node[x].ch[op ^ 1] = fx, node[fx].fa = x;
push_up(fx);
push_up(x);
}
void splay(int x)//splay基本操作,注意先下放标记
{
push(x);
for(int fx = node[x].fa; get(x) != -1; rotate(x), fx = node[x].fa)
{
if(get(fx)!=-1)
{
(get(x)==get(fx))?rotate(fx):rotate(x);
}
}
}
void access(int x)//最重要操作,即我在前一篇说的更改轻重链,它能将x节点与整棵树根(不是所在splay)连到一个重链里
//并且x节点和儿子都以虚边连接以支持换根
{
for(int y = 0; x;)
{
splay(x);//先转到当前splay的根
node[x].ch[1] = y;//右儿子断开,变为上次转到splay的点
push_up(x);
y = x, x = node[x].fa;//更新
}
}
void make_root(int x)//换根操作,令x成为整棵树根(基于access后x为所在splay最深点性质)
{
access(x), splay(x), node[x].rev ^= 1;//打标记是因为x变为根后它的深度要变为最小,因此需翻转
}
void split(int x, int y)//把x与y的路径组成一个以y为根的splay
{
make_root(x), access(y), splay(y);
}
void cut(int x,int y)//把x与y分离
{
split(x,y);
node[x].fa = node[y].ch[0] = 0;
}
void line(int x,int y)//连接x,y
{
node[x].fa=y;
}
int find(int x)//查找x点所在树的根节点
{
for(access(x), splay(x); node[x].ch[0]; x = node[x].ch[0]);
return x;
}
int find_ps(int x)
{
split(x, 1);
if(node[1].mx <= 0)
return -1;
return node[1].ps;
}
}
using namespace LCT;
int idx[N];
int qq;
pll ask(int x, int y)
{
ll res = 0, num = 0;
while(y)
{
int ps = find_ps(x);
//cerr << ps << ' ' << node[ps].val << ' ' << node[ps].las << '\n';
if(ps < 0)
break;
int tmp = min(node[ps].las, y);
node[ps].las -= tmp;
y -= tmp;
num += tmp;
res += 1LL * node[ps].val * tmp;
if(node[ps].las == 0)
{
make_root(ps);
node[ps].las = 0;
node[ps].val = -1;
node[ps].mx = -1;
}
}
return pll(res, num);
}
void sol()
{
tot = N - 1;
init();
idx[0] = 1;
cin >> qq;
int x, y;
cin >> x >> y;
node[++tot].in(x, y);
ll ans = 0;
//cerr << node[1].fa << '\n';
for(int i = 1; i <= qq; i++)
{
int op;
cin >> op;
if(op == 1)
{
int fa;
cin >> fa;
fa = (fa + ans) % i;
fa = idx[fa];
cin >> x >> y;
idx[i] = ++tot;
node[tot].in(x, y);
//cerr << ">>" << fa << ' ' << x << ' ' << y << '\n';
line(tot, fa);
}
else
{
cin >> x >> y;
pll cur = ask(idx[x], y);
//cerr << "!!" << cur.sc << ' ' << cur.fi << '\n';
cout << cur.sc << ' ' << cur.fi << '\n';
ans = cur.sc + cur.fi;
}
}
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);
// int tt;
// cin >> tt;
// while(tt--)
sol();
}