[SDOI2018] 战略游戏 点双联通分量 虚树

版权声明:想转就转吧,注明出处就行 括弧笑 https://blog.csdn.net/BlackJack_/article/details/80362694

这里写图片描述

这是一道只要前置技能解锁足够即可AC的题
可以BJ不够 泪
我怎么可以不会点双呢。。。

点双缩点
每次建虚树
做完了

#include<cmath>
#include<ctime>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<iomanip>
#include<vector>
#include<string>
#include<bitset>
#include<queue>
#include<set>
#include<map>
using namespace std;

typedef long long ll;

inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch<='9'&&ch>='0'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
    return x*f;
}
void print(int x)
{if(x<0)putchar('-'),x=-x;if(x>=10)print(x/10);putchar(x%10+'0');}

const int N=200100;

int n,m,tot;

namespace graph
{
    int last[N<<1],ecnt;
    struct EDGE{int to,nt;}e[N<<3];
    inline void add(int u,int v)
    {e[++ecnt]=(EDGE){v,last[u]};last[u]=ecnt;}

    int dfn[N<<1],val[N<<1],dep[N<<1],tim;
    int anc[N<<1][20];

    void dfs(int u)
    {
        dfn[u]=++tim;
        for(int i=1;(1<<i)<=dep[u];++i)
            anc[u][i]=anc[anc[u][i-1]][i-1];
        for(int i=last[u],v;i;i=e[i].nt)
            if((v=e[i].to)!=anc[u][0])
                dep[v]=dep[u]+1,
                val[v]+=val[u],
                anc[v][0]=u,
                dfs(v);
    }

    inline int getlca(int u,int v)
    {
        if(dep[u]<dep[v]) swap(u,v);
        int len(dep[u]-dep[v]);
        for(int i=0;(1<<i)<=len;++i)
            if((1<<i)&len)
                u=anc[u][i];
        if(u==v) return u;
        for(int i=19;~i;--i)
            if(anc[u][i]!=anc[v][i])
                u=anc[u][i],v=anc[v][i];
        return anc[u][0];
    }

    void init()
    {
        ecnt=tim=0;
        memset(val,0,sizeof(val));
        memset(dep,0,sizeof(dep));
        memset(anc,0,sizeof(anc));
        memset(last,0,sizeof(last));
    }
}

int last[N],ecnt;
struct EDGE{int to,nt;}e[N<<2];
inline void add(int u,int v)
{e[++ecnt]=(EDGE){v,last[u]};last[u]=ecnt;}

int dfn[N],low[N],tim;
int st[N],top;

void tarjan(int u)
{
    dfn[u]=low[u]=++tim,
    graph::val[u]=1,
    st[++top]=u;
    for(int i=last[u],v;i;i=e[i].nt)
        if(!dfn[v=e[i].to])
        {
            tarjan(v),low[u]=min(low[u],low[v]);
            if(low[v]>=dfn[u])
            {
                tot++;
                int tmp(0);
                do
                {
                    tmp=st[top--];
                    graph::add(tmp,tot),
                    graph::add(tot,tmp);
                }while(tmp!=v);
                graph::add(u,tot),graph::add(tot,u);
            }
        }
        else low[u]=min(low[u],dfn[v]);
}

inline bool cmp(int x,int y)
{return graph::dfn[x]<graph::dfn[y];}

inline void insert(int &res,int x,int y)
{res+=graph::val[x]-graph::val[y];}

int get_ans(int *s)
{
    register int i,grd,res(0);
    sort(s+1,s+1+s[0],cmp);
    int root=graph::getlca(s[1],s[s[0]]);
    st[1]=root,top=1;
    for(i=1;i<=s[0];++i)
    {
        grd=graph::getlca(s[i],st[top]);
        while(1)
        {
            if(graph::dep[st[top-1]]<=graph::dep[grd])
            {
                insert(res,st[top],grd);top--;
                if(st[top]!=grd) st[++top]=grd;
                break;
            }
            insert(res,st[top],st[top-1]),top--;
        }
        if(s[i]!=st[top]) st[++top]=s[i];
    }
    while(top>1) insert(res,st[top],st[top-1]),top--;
    return res+(root<=n)-s[0];
}

int s[N];

void solve()
{
    register int i,u,v;
    tot=n=read(),m=read();
    for(i=1;i<=m;++i)
        u=read(),v=read(),
        add(u,v),add(v,u);

    tarjan(1);
    graph::dfs(1);

    int Q=read();
    while(Q--)
    {
        s[0]=read();
        for(i=1;i<=s[0];++i) s[i]=read();
        print(get_ans(s)),puts("");
    }
}

void initial()
{
    graph::init();
    tim=top=ecnt=0;
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(last,0,sizeof(last));
}

int main()
{
    int T=read();
    while(T--) initial(),solve();
    return 0;
}
展开阅读全文

没有更多推荐了,返回首页