【BZOJ5329】战略游戏(SDOI2018)-圆方树+虚树

测试地址:战略游戏
做法:本题需要用到圆方树+虚树。
显而易见的是,两个点之间路径的必经点,就等于它们之间路径上的所有割点。因此我们很快想到建出圆方树,这样两点间路径上所有的圆点(除去两端)就是对应的割点。而询问一个集合,问能切开集合中某两个点的所有点,那就是求所有这些点两两之间路径的并,答案就等于并集中圆点的数目减去询问集合的大小(因为询问集合内的点不能算作答案)。
树上一个点集两两之间路径的并也是一棵树,用更加熟悉的一个词来说,是一棵虚树,因此我们在圆方树上求虚树即可,时间复杂度为O(|S|log|S|)
以下是本人代码:

#include <bits/stdc++.h>
using namespace std;
const int N=200010;
int T,n,m,q;
int first[N],firstt[N],tot,totpbc;
int low[N],dfn[N],st[N],top,tim,pos[N],p[N<<1],s;
int fa[N][20],dep[N],dis[N];
bool vis[N];
struct edge
{
    int v,next;
}e[N<<1],t[N<<1];

void insert(int a,int b)
{
    e[++tot].v=b;
    e[tot].next=first[a];
    first[a]=tot;
}

void insertt(int a,int b)
{
    t[++tot].v=b;
    t[tot].next=firstt[a];
    firstt[a]=tot;
}

void tarjan(int v,int fa)
{
    low[v]=dfn[v]=++tim;
    st[++top]=v;
    vis[v]=1;
    for(int i=first[v];i;i=e[i].next)
        if (e[i].v!=fa)
        {
            if (!vis[e[i].v])
            {
                tarjan(e[i].v,v);
                low[v]=min(low[v],low[e[i].v]);
                if (low[e[i].v]>=dfn[v])
                {
                    ++totpbc;
                    insertt(v,totpbc);
                    do
                    {
                        insertt(totpbc,st[top]);
                    }while(st[top--]!=e[i].v);
                }
            }
            else low[v]=min(low[v],dfn[e[i].v]);
        }
}

void dfs(int v)
{
    pos[v]=++tim;
    for(int i=firstt[v];i;i=t[i].next)
    {
        fa[t[i].v][0]=v;
        dep[t[i].v]=dep[v]+1;
        dis[t[i].v]=dis[v]+(t[i].v<=n);
        dfs(t[i].v);
    }
}

bool cmp(int a,int b)
{
    return pos[a]<pos[b];
}

int lca(int x,int y)
{
    if (dep[x]<dep[y]) swap(x,y);
    for(int i=18;i>=0;i--)
        if (dep[fa[x][i]]>=dep[y]) x=fa[x][i];
    if (x==y) return x;
    for(int i=18;i>=0;i--)
        if (fa[x][i]!=fa[y][i])
            x=fa[x][i],y=fa[y][i];
    return fa[x][0];
}

void build()
{
    sort(p+1,p+s+1,cmp);
    for(int i=1;i<s;i++)
        p[s+i]=lca(p[i],p[i+1]);
    sort(p+1,p+(s<<1),cmp);

    int tot=1;
    for(int i=1;i<(s<<1)-1;i++)
        if (p[i]!=p[i+1]) p[++tot]=p[i+1];

    int ans;
    top=ans=0;
    for(int i=1;i<=tot;i++)
    {
        while(top&&lca(st[top],p[i])!=st[top]) top--;
        st[++top]=p[i];
        if (top>1) ans+=dis[st[top]]-dis[st[top-1]];
    }
    if (p[1]<=n) ans++;
    printf("%d\n",ans-s);
}

int main()
{
    scanf("%d",&T);
    while(T--)
    {
        memset(first,0,sizeof(first));
        memset(firstt,0,sizeof(firstt));
        tot=0;

        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            insert(a,b),insert(b,a);
        }

        tot=top=tim=0,totpbc=n;
        memset(vis,0,sizeof(vis));
        tarjan(1,0);

        memset(fa,0,sizeof(fa));
        tim=0;
        dis[1]=1,dep[1]=0,dep[0]=-1;
        dfs(1);
        for(int i=1;i<=18;i++)
            for(int j=1;j<=totpbc;j++)
                fa[j][i]=fa[fa[j][i-1]][i-1];

        scanf("%d",&q);
        for(int i=1;i<=q;i++)
        {
            scanf("%d",&s);
            for(int j=1;j<=s;j++)
                scanf("%d",&p[j]);
            build();
        }
    }

    return 0;
}
posted @ 2018-06-19 20:03  Maxwei_wzj  阅读(129)  评论(0编辑  收藏  举报