【BZOJ】P3772 精神污染

DFS序+主席树

其实这还可以用树状数组搞的(伟大的XSH大佬提出的)
But,本人苦调3个多小时,调不出来,最后无奈一查题解——woc,主席树!
暴力一发,A了。。。
那么来看看主席树是怎么PC过去的。

维护什么?

  • 首先,想一下若路径A能被路径B包含,那么A的两端点就在路径B上。(无需讨论链和折线的情况)
  • 那么,对于每个路径一端点开个vector,把另一端点加入其中。
  • 然后,主席树上,我们就储存下A路径,查找时就看一下有多少条可以被所查路径包含。
  • 这样,主席树就很好维护了。每个节点先以父亲节点为前缀构建,维护每条路径中的另一端点,(注意了,由于这是以父亲节点为前缀的,那么祖先存的路径中一端点已经被当前节点包含了)
  • 由于需要把树转换成区间,我们需要用出入栈序(回溯时序号也要+1)

怎样修改?

那么,加入一端点时,只有在其子树中的点才能包含其(不需要讨论链的情况)。因此,将in[x]++,out[x]--。(单点修改)

怎样查询

  • 查询时,由于每个节点的主席树是维护到根节点的,可以先把所查路径变成两条链(x,LCA(x,y))和(y,LCA(x,y))两部分。
  • 再对于每个链(a,b),那么其的值就是Query(x,a,b)+Query(y,a,b)-Query(LCA(x,y),a,b)-Query(pre_LCA(x,y),a,b)(Query(t,a,b)表示在t节点的主席树上,查询链(a,b))。
    注意:这里不要混淆了。链(a,b)表示一个区间,我们需要在以t节点的主席树上查询有多少个点在此区间中(即使是链(x,LCA(x,y)),其中在以y节点的主席树上也有节点,因为两者是不等价的。另外,查询链需要用到作差法)

这样,这道题差不多就解决了。

代码:

#include<bits/stdc++.h>
#define ll long long
const int MAXN=2e5+20;
using namespace std;
int n,m,rt[MAXN],lson[MAXN*20],rson[MAXN*20],L[MAXN],R[MAXN],mark,dfsn,lg[MAXN],pre[MAXN][20],deep[MAXN],Askx[MAXN],Asky[MAXN];
ll sum[MAXN*20],ans;
vector<int> G[MAXN],A[MAXN];
void DFS_BL(int x,int fa){
    L[x]=++dfsn;
    pre[x][0]=fa;
    deep[x]=deep[fa]+1;
    for(int i=1;(1<<i)<=deep[x];i++)pre[x][i]=pre[pre[x][i-1]][i-1];
    for(int i=0;i<G[x].size();i++){
        int t=G[x][i];
        if(t==fa)continue;
        DFS_BL(t,x);
    }
    R[x]=++dfsn;
}
int LCA(int x,int y){
    if(deep[x]<deep[y])swap(x,y);
    while(deep[x]>deep[y])x=pre[x][lg[deep[x]-deep[y]]-1];
    if(x==y)return x;
    for(int k=lg[deep[x]]-1;k>=0;k--){
        if(pre[x][k]==pre[y][k])continue;
        x=pre[x][k],y=pre[y][k];
    }
    return pre[x][0];
}
void insert(int &node,int ot,int L,int R,int num,int pos){
    node=++mark;
    sum[node]=sum[ot]+num;
    lson[node]=lson[ot];
    rson[node]=rson[ot];
    if(L==R)return;
    int mid=(L+R)>>1;
    if(pos<=mid)insert(lson[node],lson[ot],L,mid,num,pos);
    else insert(rson[node],rson[ot],mid+1,R,num,pos);
}
ll query(int nodex,int nodey,int nodef,int nodeff,int L,int R,int st,int ed){
    if(st==L&&ed==R)return sum[nodex]+sum[nodey]-sum[nodef]-sum[nodeff];
    int mid=(L+R)>>1;
    if(ed<=mid)return query(lson[nodex],lson[nodey],lson[nodef],lson[nodeff],L,mid,st,ed);
    else if(st>=mid+1)return query(rson[nodex],rson[nodey],rson[nodef],rson[nodeff],mid+1,R,st,ed);
    else return query(lson[nodex],lson[nodey],lson[nodef],lson[nodeff],L,mid,st,mid)+query(rson[nodex],rson[nodey],rson[nodef],rson[nodeff],mid+1,R,mid+1,ed);
}
void DFS(int x,int fa){
    rt[x]=rt[fa];
    int tmp;
    for(int i=0;i<A[x].size();i++){
        insert(tmp,rt[x],1,dfsn,1,L[A[x][i]]);
        rt[x]=tmp;
        insert(tmp,rt[x],1,dfsn,-1,R[A[x][i]]);
        rt[x]=tmp;
    }
    for(int i=0;i<G[x].size();i++){
        int t=G[x][i];
        if(t==fa)continue;
        DFS(t,x);
    }
}
int main(){
    for(int i=1;i<=MAXN-10;i++)lg[i]=lg[i-1]+((1<<lg[i-1])==i);
    scanf("%d %d",&n,&m);
    for(int i=1;i<=n-1;i++){
        int x,y;
        scanf("%d %d",&x,&y);
        G[x].push_back(y);
        G[y].push_back(x);
    }
    DFS_BL(1,0);
    for(int i=1;i<=m;i++){
        int x,y;
        scanf("%d %d",&x,&y);
        Askx[i]=x,Asky[i]=y;
        A[x].push_back(y);
    }
    DFS(1,0);
    for(int i=1;i<=m;i++){
        int x=Askx[i],y=Asky[i];
        int lca=LCA(x,y);
        ans+=query(rt[x],rt[y],rt[lca],rt[pre[lca][0]],1,dfsn,L[lca],L[x]);
        ans+=query(rt[x],rt[y],rt[lca],rt[pre[lca][0]],1,dfsn,L[lca],L[y]);
        ans-=query(rt[x],rt[y],rt[lca],rt[pre[lca][0]],1,dfsn,L[lca],L[lca]);
        ans--;
    }
    ll tot=1LL*m*(m-1)/2;
    ll gcd=__gcd(tot,ans);
    printf("%lld/%lld",ans/gcd,tot/gcd);
    return 0;
}

原文地址:https://www.cnblogs.com/SillyTieT/p/11197856.html