P3950 - 部落冲突(树链剖分)

此题用树链剖分处理

操作1:询问p点的工人是否能够到达q点

操作2:p点部落与q点开战

操作3:第x次战争结束

当p点与q点开战时工人无法通过两点间的路径,因此我们可以令开战时的路径权值为1,未开战时为0,则工人能到达q点的条件是:当且仅当p点到q点的路径权值和为0

需要注意的是,树链剖分是用来处理树上的点值的,我们可以将边权值同一下放到深度较大的那一点上。具体处理权值时,我们需要在增加点权值时减去两点间的LCA(减去点权值时加上LCA),查询时减去两点间的LCA,此时可以把边权值的维护转换为点权值的维护,于是可以用树链剖分解决

 

 

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
using namespace std;
#define lid (id << 1)
#define rid (id << 1) | 1
typedef long long LL;

const int maxn=400010;

struct Edge{
    int nex;
    int to;
}E[2*maxn];

int rt,n,m,r,cnt,tot;
int a[2*maxn];
int head[maxn];
int f[maxn];//节点u的父亲节点 
int d[maxn];//节点u的深度 
int size[maxn];//以u为根的子树节点个数
int son[maxn];//重儿子 
int rk[maxn];//当前dfs标号在树中所对应的节点 
int top[maxn];//当前节点所在链的顶端节点 
int id[maxn];//树中每个节点剖分以后的新编号(决定DFS执行顺序) 

int rem[maxn];
int mod=0x3f3f3f3f;

struct seg_tree 
{
    LL l, r;
    LL lazy;
    long long sum;
} tree[maxn << 2];

void build(LL id, LL l, LL r) 
{
    tree[id].l = l;
    tree[id].r = r;
    if (l == r)
    {
        tree[id].sum = a[rk[l]];
        return;
    }
    int mid = (l + r) >> 1;
    build(lid, l, mid);
    build(rid, mid + 1, r);
    tree[id].sum = (tree[lid].sum + tree[rid].sum)%mod;
}

void pushdown(LL id) 
{
    if (tree[id].lazy != 0 && tree[id].l != tree[id].r) 
    {
        LL val = tree[id].lazy;
        (tree[lid].lazy += val)%=mod;
        (tree[rid].lazy += val)%=mod;
        tree[lid].sum += val * (tree[lid].r - tree[lid].l + 1);
        tree[rid].sum += val * (tree[rid].r - tree[rid].l + 1);
        tree[id].lazy = 0;
    }
}


void add(LL id, LL val,LL l, LL r) 
{
    pushdown(id);
    if (tree[id].l == l && tree[id].r == r) 
    {
        tree[id].lazy += val;
        tree[id].sum += val * (r - l + 1);
        tree[id].sum%=mod;
        return;
    }
    LL mid = (tree[id].l + tree[id].r) >> 1;
    if (mid >= r)
        add(lid, val, l, r);
    else if (mid < l)
        add(rid, val, l, r);
    else
    {
        add(lid, val, l, mid);
        add(rid, val, mid + 1, r);
    }
    tree[id].sum = (tree[lid].sum + tree[rid].sum)%mod;
}

long long query(LL id,LL l, LL r) 
{
    pushdown(id);
    if (tree[id].l == l && tree[id].r == r)
        return tree[id].sum;
    int mid = (tree[id].l + tree[id].r) >> 1;
    if (mid >= r)
        return query(lid, l, r)%mod;
    else if (mid < l)
        return query(rid, l, r)%mod;
    else return (query(lid, l, mid) + query(rid, mid + 1, r))%mod;
}

void addedge(int u,int v)
{
    E[++tot].nex=head[u];
    E[tot].to=v;
    head[u]=tot;
}

void dfs1(int u,int fa,int depth)
{
    f[u]=fa;
    d[u]=depth;
    size[u]=1;
    for(int i=head[u];i;i=E[i].nex)
    {
        int v=E[i].to;
        if(v==fa)continue;
        dfs1(v,u,depth+1);
        size[u]+=size[v];
        if(size[v]>size[son[u]])
        {
            son[u]=v;
        }//选取重儿子
    }
}

void dfs2(int u,int t)//t为重链顶端,连接重链,记录dfs,处理top,id,rk以保证dfs序连续 
{
    top[u]=t;
    id[u]=++cnt;//记录dfs序
    rk[cnt]=u;//记录相应dfs序的节点
    if(!son[u])
    {
        return;
    }
    dfs2(son[u],t);
    for(int i=head[u];i;i=E[i].nex)
    {
        int v=E[i].to;
        if(v!=son[u]&&v!=f[u])
        dfs2(v,v); //轻链底端结点即为重链的顶端 
    }
}

int sum(int x,int y)
{
    int ans=0;
    int fx=top[x];
    int fy=top[y];
    while(top[x]!=top[y])
    {
        if(d[top[x]]<d[top[y]])swap(x,y);
        ans+=query(1,id[top[x]],id[x]);
        x=f[top[x]];
    }
    if(id[x]>id[y])swap(x,y);
    ans+=query(1,id[x],id[y]);
    ans-=query(1,id[x],id[x]);
    return ans;
}

void update(int x,int y,int c)
{
    while(top[x]!=top[y])
    {
        if(d[top[x]]<d[top[y]])
        {
            swap(x,y);
        }
        add(1,c,id[top[x]],id[x]);
        x=f[top[x]];
    }
    if(id[x]>id[y])swap(x,y);
    add(1,c,id[x],id[y]); 
    add(1,-c,id[x],id[x]);
}

int tot1[maxn];
int tot2[maxn];
int toto;

int main(){
    scanf("%d%d",&n,&m);
    r=1;
    for(int i=1;i<=n;i++)
    {
    a[i]=0;
    }
    for(int i=1;i<n;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        addedge(x,y);
        addedge(y,x);
    }
    dfs1(r,0,1);
    dfs2(r,r);
    rt=1;
    build(1,1,n);
    for(int i=1;i<=m;i++)
    {
        char s[5];
        int x,y,z;
        cin>>s;
        if(s[0]=='C')
        {
            scanf("%d%d",&x,&y);
            update(x,y,1);
            tot1[++toto]=x;
            tot2[toto]=y; 
        }
        if(s[0]=='Q'){
            scanf("%d%d",&x,&y);
            if((sum(x,y))!=0)
            {
                printf("No\n");
            }
            else printf("Yes\n");
        }
        if(s[0]=='U')
        {
            scanf("%d",&x);
            update(tot1[x],tot2[x],-1);
        }
    }
    return 0;
    //处理深度d以及父节点f 
}

 

posted @ 2021-09-17 16:15  lemonGJacky  阅读(52)  评论(0)    收藏  举报