[Codeforces757G]Can Bash Save the Day?——动态点分治(可持久化点分树)

题目链接:

Codeforces757G

 题目大意:给出一棵n个点的树及一个1~n的排列pi,边有边权,有q次操作:

1 l r x 求 $\sum\limits_{i=l}^{r}dis(p_{i},x)$

2 x $swap(p_{x},p_{x+1})$

$n,q<=2*10^5$,强制在线

如果多次询问一个点到所有点的距离和,我们可以点分树解决,在点分树上每个点x维护点分树上x子树中的所有点到x的距离和及所有点到x父节点的距离和,每次询问往根爬容斥一下求和即可。如果没有修改操作我们依旧可以每个点对pi建线段树来区间求和,但有了修改操作在修改时时间复杂度会爆炸。我们依据可持久化线段树区间查询的原理也可以对点分树进行可持久化,按照pi的顺序每个版本往点分树中插入一个点的信息,建出对应的一条链并将链上点没建出的子节点连向上一个版本的对应节点,每个点同样维护上述两个信息,但如果直接连接可能一个点会有许多儿子,这样时间复杂度就不对了,因此我们将多叉树转二叉树(具体操作详见边分治讲解),这样一个点的出边最多只有三个,点分树上的子节点数也就最多只有三个。具体的可持久化点分树插入和查询操作参见动态点分治讲解。因为可持久化点分树每个版本相当于保存了一个前缀和,所以查询时直接用r版本的答案减l-1版本的答案即可。对于修改操作,可以发现实际需要修改的就只要x这个版本,那么我们重建x版本的点分树即可。

#include<set>
#include<map>
#include<queue>
#include<cmath>
#include<stack>
#include<cstdio>
#include<vector>
#include<bitset>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
#define pr pair<int,ll>
using namespace std;
ll z;
ll ans;
int op;
int cnt;
int tot;
int x,y;
int l,r;
int dfn;
int rot;
int num;
int n,m,q;
ll d[400010];
int a[200010];
int p[400010];
int val[800010];
int to[800010];
int lg[800010];
int mx[400010];
int dep[400010];
int vis[400010];
int size[400010];
int nxt[800010];
int head[400010];
int g[800010][20];
int root[200010];
vector<pr>v[200010];
vector<int>pre[400010];
struct miku
{
    int son[3];
    int res;
    int lty;
    ll sum;
    ll fsum;
}s[12000010];
void add(int x,int y,ll z)
{
    tot++;
    nxt[tot]=head[x];
    head[x]=tot;
    to[tot]=y;
    val[tot]=z;
}
void rebuild(int x,int fa)
{
    int len=v[x].size();
    int last=0;
    int tmp=0;
    for(int i=0;i<len;i++)
    {
        int to=v[x][i].first;
        int val=v[x][i].second;
        if(to==fa)
        {
            continue;
        }
        tmp++;
        if(tmp==1)
        {
            add(x,to,val);
            add(to,x,val);
            last=x;
        }
        else if(tmp==len-(x!=1))
        {
            add(last,to,val);
            add(to,last,val);
        }
        else
        {
            m++;
            add(last,m,0);
            add(m,last,0);
            last=m;
            add(m,to,val);
            add(to,m,val);
        }
    }
    for(int i=0;i<len;i++)
    {
        if(v[x][i].first==fa)
        {
            continue;
        }
        rebuild(v[x][i].first,x);
    }
}
void dfs(int x,int fa)
{
    g[++dfn][0]=x;
    p[x]=dfn;
    for(int i=head[x];i;i=nxt[i])
    {
        if(to[i]!=fa)
        {
            d[to[i]]=d[x]+1ll*val[i];
            dfs(to[i],x);
            g[++dfn][0]=x;
        }
    }
}
int mn(int x,int y)
{
    return d[x]<d[y]?x:y;
}
void ST()
{
    for(int i=2;i<=dfn;i++)
    {
        lg[i]=lg[i>>1]+1;
    }
    for(int j=1;j<=19;j++)
    {
        for(int i=1;i+(1<<j)-1<=dfn;i++)
        {
            g[i][j]=mn(g[i][j-1],g[i+(1<<(j-1))][j-1]);
        }
    }
}
ll lca(int x,int y)
{
    x=p[x],y=p[y];
    if(x>y)
    {
        swap(x,y);
    }
    int len=lg[y-x+1];
    return mn(g[x][len],g[y-(1<<len)+1][len]);
}
ll dis(int x,int y)
{
    return d[x]+d[y]-(d[lca(x,y)]<<1);
}
void getroot(int x,int fa)
{
    size[x]=1;
    mx[x]=0;
    for(int i=head[x];i;i=nxt[i])
    {
        if(to[i]!=fa&&!vis[to[i]])
        {
            getroot(to[i],x);
            size[x]+=size[to[i]];
            mx[x]=max(mx[x],size[to[i]]);
        }
    }
    mx[x]=max(mx[x],num-size[x]);
    if(mx[x]<mx[rot])
    {
        rot=x;
    }
}
void partation(int x)
{
    vis[x]=1;
    s[x].lty=x;
    for(int i=head[x];i;i=nxt[i])
    {
        if(!vis[to[i]])
        {
            
            num=size[to[i]];
            rot=0;
            getroot(to[i],0);
            dep[rot]=dep[x]+1;
            for(int j=0;j<dep[x];j++)
            {
                pre[rot].push_back(pre[x][j]);
            }
            for(int j=0;j<=2;j++)
            {
                if(!s[x].son[j])
                {
                    s[x].son[j]=rot;
                    pre[rot].push_back(j);
                    break;
                }
            }
            partation(rot);
        }
    }
}
void insert(int nex,int &rt,int x,int fa)
{
    rt=++cnt;
    int now=s[nex].lty;
    memcpy(s[rt].son,s[nex].son,sizeof(s[rt].son));
    s[rt].lty=now;
    s[rt].res=s[nex].res+1;
    s[rt].sum=s[nex].sum+dis(now,x);
    s[rt].fsum=s[nex].fsum;
    if(fa)
    {
        s[rt].fsum+=dis(s[fa].lty,x);
    }
    if(s[rt].lty==x)
    {
        return ;
    }
    insert(s[nex].son[pre[x][dep[now]]],s[rt].son[pre[x][dep[now]]],x,rt);
}
ll query(int rt,int x)
{
    int now;
    ll ret=0;
    for(int i=0;i<dep[x];i++)
    {
        now=s[rt].son[pre[x][i]];
        ret+=(s[rt].res-s[now].res)*dis(s[rt].lty,x)+(s[rt].sum-s[now].fsum);
        rt=now;
    }
    ret+=s[rt].sum;
    return ret;
}
int main()
{
    scanf("%d%d",&n,&q);
    m=n;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
    }
    for(int i=1;i<n;i++)
    {
        scanf("%d%d%lld",&x,&y,&z);
        v[x].push_back(make_pair(y,z));
        v[y].push_back(make_pair(x,z));
    }
    rebuild(1,0);
    dfs(1,0);
    ST();
    num=m;
    cnt=m;
    mx[0]=1<<30;
    getroot(1,0);
    root[0]=rot;
    partation(rot);
    for(int i=1;i<=n;i++)
    {
        insert(root[i-1],root[i],a[i],0);
    }
    while(q--)
    {
        scanf("%d",&op);
        if(op==1)
        {
            scanf("%d%d%d",&l,&r,&x);
            l^=ans;
            r^=ans;
            x^=ans;
            ans=query(root[r],x)-query(root[l-1],x);
            printf("%lld\n",ans);
            ans%=(1<<30);
        }
        else
        {
            scanf("%d",&x);
            x^=ans;
            swap(a[x],a[x+1]);
            insert(root[x-1],root[x],a[x],0);
        }
    }
}
posted @ 2018-12-26 19:16  The_Virtuoso  阅读(855)  评论(0编辑  收藏  举报