bzoj 3159: 决战 (树链剖分+splay)

题目描述

传送门

题解

其实就是之前用线段树维护的东西改成用splay维护
比较麻烦的就是翻转操作,需要把翻转的位置提出来插入的另一个splay中,翻转后再插回去
具体的细节还是见代码吧。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 100003
#define inf 1000000000
#define LL long long 
using namespace std;
int tot,cnt,sz,n,m,k,point[N],v[N],nxt[N],belong[N],deep[N],son[N],f[N];
int ch[N][3],root,rt,a[N],pos[N],fa[N],rev[N];
LL mx[N],mn[N],sum[N],val[N],tag[N],size[N];
struct data{
    int x,l,r;
}c[100];
char s[20];
void add(int x,int y)
{
    tot++; nxt[tot]=point[x]; point[x]=tot; v[tot]=y;
    tot++; nxt[tot]=point[y]; point[y]=tot; v[tot]=x;
}
void dfs(int x,int f1)
{
    deep[x]=deep[f1]+1; size[x]=1;
    for (int i=point[x];i;i=nxt[i]){
        if (v[i]==f1) continue;
        f[v[i]]=x;
        dfs(v[i],x);
        size[x]+=size[v[i]];
        if (size[v[i]]>size[son[x]]) son[x]=v[i];
    }
}
void dfs1(int x,int chain)
{
    pos[x]=++sz; a[sz]=x; belong[x]=chain;
    if (!son[x]) return;
    dfs1(son[x],chain);
    for (int i=point[x];i;i=nxt[i])
     if (v[i]!=f[x]&&v[i]!=son[x]) dfs1(v[i],v[i]);
}
void change(int now,LL delta)
{
    val[now]+=delta; mn[now]+=delta; mx[now]+=delta;
    sum[now]+=size[now]*delta; tag[now]+=delta;
}
void pushdown(int now)
{
    if (tag[now]) {
        change(ch[now][0],tag[now]);
        change(ch[now][1],tag[now]);
        tag[now]=0;
    }
    if (rev[now]) {
        swap(ch[now][1],ch[now][0]);
        rev[ch[now][0]]^=1; rev[ch[now][1]]^=1; 
        rev[now]=0;
    }
}
void update(int now)
{
    int l=ch[now][0]; int r=ch[now][1]; tag[now]=0;
    sum[now]=mx[now]=mn[now]=val[now]; size[now]=1;
    if (l) sum[now]+=sum[l],size[now]+=size[l],
           mx[now]=max(mx[now],mx[l]),mn[now]=min(mn[now],mn[l]);
    if (r) sum[now]+=sum[r],size[now]+=size[r],
           mx[now]=max(mx[now],mx[r]),mn[now]=min(mn[now],mn[r]);
}
int get(int x)
{
    return ch[fa[x]][1]==x;
}
void rotate(int x)
{
    int y=fa[x]; int z=fa[y];
    pushdown(y); pushdown(x); int which=get(x);
    ch[y][which]=ch[x][which^1]; fa[ch[x][which^1]]=y;
    if (z) ch[z][ch[z][1]==y]=x;
    fa[x]=z; fa[y]=x; ch[x][which^1]=y; 
    update(y); update(x);
}
void splay(int x,int tar,int &RT)
{
    for (int f1;(f1=fa[x])!=tar;rotate(x))
     if (fa[f1]!=tar) rotate(get(x)==get(f1)?f1:x);
    if (!tar) RT=x;
}
int find(int now,int x)
{
    while (true) {
        pushdown(now);
        if (size[ch[now][0]]>=x) now=ch[now][0];
        else {
            x-=size[ch[now][0]];
            if (x==1) return now;
            x--;
            now=ch[now][1];
        }
    }
}
int build(int l,int r)
{
    if (l>r) return 0;
    if (l==r) {
        ++cnt; size[cnt]=1;
        return cnt; 
    }
    int mid=(l+r)/2; int now=++cnt; 
    ch[now][0]=build(l,mid-1); fa[ch[now][0]]=now;
    ch[now][1]=build(mid+1,r); fa[ch[now][1]]=now;
    update(now);
    return now;
}
void init()
{
    rt=++cnt; int now=++cnt;
    ch[rt][1]=now; fa[now]=rt; size[now]=1;
    size[rt]=2; 
}
int work(int x,int y)
{
    int aa=find(root,x-1); int bb=find(root,y+1);
    splay(aa,0,root); splay(bb,aa,root);
    return ch[ch[root][1]][0];
}
void solve(int x,int y,int z)
{
    while (belong[x]!=belong[y]){
        if (deep[belong[x]]<deep[belong[y]]) swap(x,y);
        int t=work(pos[belong[x]],pos[x]);
        change(t,z);
        x=f[belong[x]];
    }
    if (deep[x]>deep[y]) swap(x,y);
    int t=work(pos[x],pos[y]);
    change(t,z);
}
LL calc(int x,int y,int opt)
{
    LL ans=(opt==3?inf:0);
    while (belong[x]!=belong[y]) {
        if (deep[belong[x]]<deep[belong[y]]) swap(x,y);
        int t=work(pos[belong[x]],pos[x]);
        if (opt==1) ans+=sum[t];
        if (opt==2) ans=max(ans,mx[t]);
        if (opt==3) ans=min(ans,mn[t]);
        x=f[belong[x]];
    }
    if (deep[x]>deep[y]) swap(x,y);
    int t=work(pos[x],pos[y]);
    if (opt==1) ans+=sum[t];
    if (opt==2) ans=max(ans,mx[t]);
    if (opt==3) ans=min(ans,mn[t]);
    return ans;
}
void invert(int x,int y)
{
    int top=1; tot=0; pushdown(rt);
    while (belong[x]!=belong[y]) {
        if (deep[belong[x]]<deep[belong[y]]) swap(x,y);
        int t=work(pos[belong[x]],pos[x]); ch[ch[root][1]][0]=0;
        fa[t]=0; update(ch[root][1]); update(root);
        ++tot; c[tot].x=pos[belong[x]]-1;
        int len=pos[x]-pos[belong[x]]+1;
        c[tot].l=top+1; c[tot].r=top+len;
        int aa=find(rt,top); int bb=find(rt,top+1);
        splay(aa,0,rt); splay(bb,aa,rt);
        ch[ch[rt][1]][0]=t; fa[t]=ch[rt][1]; rev[t]^=1;
        update(ch[rt][1]); update(rt);
        top=c[tot].r;
        x=f[belong[x]];
    }
    if (deep[x]>deep[y]) swap(x,y);
    int t=work(pos[x],pos[y]);
    ch[ch[root][1]][0]=0;
    fa[t]=0; update(ch[root][1]); update(root);
    ++tot; c[tot].x=pos[x]-1;
    int len=pos[y]-pos[x]+1;
    c[tot].l=top+1; c[tot].r=top+len;
    int aa=find(rt,top); int bb=find(rt,top+1);
    splay(aa,0,rt); splay(bb,aa,rt);
    ch[ch[rt][1]][0]=t; fa[t]=ch[rt][1]; rev[t]^=1;
    update(ch[rt][1]); update(rt);
    top=c[tot].r;
    rev[rt]^=1;
    for (int i=tot;i>=1;i--) {
        int aa=find(rt,c[i].l-1); int bb=find(rt,c[i].r+1);
        splay(aa,0,rt); splay(bb,aa,rt);
        int t=ch[ch[rt][1]][0];
        ch[ch[rt][1]][0]=0; fa[t]=0; update(ch[rt][1]); update(rt);
        aa=find(root,c[i].x); bb=find(root,c[i].x+1);
        splay(aa,0,root); splay(bb,aa,root);
        ch[ch[root][1]][0]=t; fa[t]=ch[root][1]; rev[t]^=1;
        update(ch[root][1]); update(root);
    }
}
int main()
{
    freopen("a.in","r",stdin);
    freopen("my.out","w",stdout);
    scanf("%d%d%d",&n,&m,&k);
    for (int i=1;i<n;i++) {
        int x,y; scanf("%d%d",&x,&y);
        add(x,y);
    }
    dfs(k,0); 
    memset(size,0,sizeof(size));
    sz=1; dfs1(k,k);
    root=build(1,n+2); init();
    for (int i=1;i<=m;i++) {
        int x,y,z; scanf("%s%d%d",s+1,&x,&y);
        if (s[1]=='I'&&s[3]=='c') scanf("%d",&z),solve(x,y,z);
        if (s[1]=='S') printf("%lld\n",calc(x,y,1));
        if (s[1]=='M'&&s[2]=='a') printf("%lld\n",calc(x,y,2));
        if (s[1]=='M'&&s[2]=='i') printf("%lld\n",calc(x,y,3));
        if (s[1]=='I'&&s[3]=='v') invert(x,y);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值