loj2587「APIO2018」铁人两项(圆方树+树形dp)

我们搞出圆方树,那么能把两个点x,y分开的点就是树上x,y之间路径上的圆点个数(不包括x,y)。
对于一个集合的点来说就是求这些点任意两点的链的并。
而这就是这些点的虚树的边长和。于是对这些点建出虚树即可。
在连一条虚树上的边(x,y)时,计算这条链对答案的贡献(不含y),最后再加上虚树根的贡献即可。
复杂度 O ( n + m + | S | ( l o g | S | + l o g n ) )

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
#define N 100010
inline int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x*f;
}
int n,m,h[N],num=0,dfn[N<<1],low[N],dfnum=0,fa[N<<1][20],Log[N<<1],dep[N<<1],w[N<<1],a[N],q[N];
struct edge{
    int to,next;
}data[N<<2];
stack<int>qq;
vector<int>Son[N<<1];
inline void tarjan(int x,int Fa){
    dfn[x]=low[x]=++dfnum;qq.push(x);w[x]=1;
    for(int i=h[x];i;i=data[i].next){
        int y=data[i].to;if(y==Fa) continue;
        if(!dfn[y]){
            tarjan(y,x);low[x]=min(low[x],low[y]);
            if(low[y]<dfn[x]) continue;++m;Son[x].push_back(m);
            while(1){
                int z=qq.top();qq.pop();Son[m].push_back(z);
                if(z==y) break;
            }continue;
        }low[x]=min(low[x],dfn[y]);
    }
}
void dfs(int x){
    for(int i=1;i<=Log[m];++i){
        if(!fa[x][i-1]) break;
        fa[x][i]=fa[fa[x][i-1]][i-1];
    }dfn[x]=++dfnum;
    for(int i=0;i<Son[x].size();++i){
        int y=Son[x][i];fa[y][0]=x;dep[y]=dep[x]+1;w[y]=w[x]+(y<=n);dfs(y);
    }
}
inline bool cmp(int x,int y){return dfn[x]<dfn[y];}
inline int lca(int x,int y){
    if(dep[x]<dep[y]) swap(x,y);
    int d=dep[x]-dep[y];
    for(int i=0;i<=Log[d];++i)
        if(d>>i&1) x=fa[x][i];
    if(x==y) return x;
    for(int i=Log[n];i>=0;--i)
        if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
    return fa[x][0];
}
inline int calc(int x,int y){return w[x]-w[y];}
int main(){
//  freopen("a.in","r",stdin);
    int tst=read();
    while(tst--){
        n=read();m=read();memset(h,0,sizeof(h));num=0;memset(dfn,0,sizeof(dfn));dfnum=0;
        memset(fa,0,sizeof(fa));memset(w,0,sizeof(w));
        while(m--){
            int x=read(),y=read();
            data[++num].to=y;data[num].next=h[x];h[x]=num;
            data[++num].to=x;data[num].next=h[y];h[y]=num;
        }m=n;tarjan(1,0);qq.pop();dfnum=0;Log[0]=-1;
        for(int i=1;i<=m;++i) Log[i]=Log[i>>1]+1;dfs(1);
        int Q=read();
        while(Q--){
            int k=read(),top=0,ans=0;
            for(int i=1;i<=k;++i) a[i]=read();sort(a+1,a+k+1,cmp);
            q[++top]=a[1];
            for(int i=1;i<=k;++i){
                int t=lca(a[i],q[top]);
                while(top&&dep[q[top]]>dep[t]){
                    int x=q[top--];
                    if(!top||dep[q[top]]<dep[t]) q[++top]=t;
                    ans+=calc(x,q[top]);
                }if(q[top]!=a[i]) q[++top]=a[i];
            }while(top){
                int x=q[top--];
                if(!top) ans+=(x<=n);
                else ans+=calc(x,q[top]);
            }printf("%d\n",ans-k);
        }for(int i=1;i<=m;++i) Son[i].clear();
    }return 0;
}               

猜你喜欢

转载自blog.csdn.net/icefox_zhx/article/details/80533647
今日推荐