码迷,mamicode.com
首页 > 其他好文 > 详细

[SDOI2018]战略游戏

时间:2019-12-22 18:40:21      阅读:87      评论:0      收藏:0      [点我收藏+]

标签:getc   register   freopen   clear   eof   遍历   tchar   圆点   连通图   

Link

Description

 给定一张\(n\)个点\(m\)条边的无向连通图。共\(q\)次询问,每次询问给出一个点集\(S\),询问有多少个点满足不在\(S\)中,且删去后使得\(S\)中的点不全在一个联通分量中。

Solution

 把圆方树建出来。那么题目询问的就是\(S\)中的点形成的极小联通子树中圆点数量减去\(|S|\)盲猜是割点数目的意思

 求圆点数量,可以将点权转边权,即 把圆点和其父亲方点之间的边权值设为1。问题转化为求边权和。而求极小联通子树的边权和,就是将\(S\)中的点按照\(dfs\)序排列,并首尾相连,计算相邻两点距离的总和,然后除以2。

 例如\(S\)中的点排序后是{\(a_1,a_2,a_3,\dots,a_k\)},则答案就是\(dis(a_1,a_2)+dis(a_2,a_3)+\dots+dis(a_{k-1},a_k)+dis(a_k,a_1)\)

 需要注意的是 如果子树中深度最浅点是圆点,那么答案还要+1,因为点权转边权是将父边权设为1,计算时并没有统计到它。

 实现的时候建出圆方树以后记得重新遍历一遍,更新新的\(dfs\)序。

Code

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
inline int read(){//be careful for long long!
    register int x=0,f=1;register char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=0;ch=getchar();}
    while(isdigit(ch)){x=x*10+(ch^'0');ch=getchar();}
    return f?x:-x;
}

const int N=1e5+10;
int n,m;
vector<int> r_E[N],E[N<<1];
#define pb(x) push_back(x)

int cnt,dfn[N<<1],low[N],dfc,stk[N],stp;
inline void Tarjan(int nw,int pa=0){
    dfn[nw]=low[nw]=++dfc;stk[++stp]=nw;
    for(int to:r_E[nw])
    if(to^pa){
        if(!dfn[to]){
        Tarjan(to,nw);
        low[nw]=min(low[nw],low[to]);
        if(dfn[nw]<=low[to]){
            ++cnt;
            for(int x=0;x^to;--stp){
            x=stk[stp];
            E[x].pb(cnt),E[cnt].pb(x);
            }
            E[nw].pb(cnt),E[cnt].pb(nw);
        }
        }
        else low[nw]=min(low[nw],dfn[to]);
    }
}

int fa[N<<1],dis[N<<1],dep[N<<1],siz[N<<1],son[N<<1],tp[N<<1],rt,qaq[N];
inline bool cmp(int a,int b){return dfn[a]<dfn[b];}
inline void Dfs_1(int nw){
    dfn[nw]=++dfc;
    siz[nw]=1;dis[nw]=dis[fa[nw]]+(nw<=n);son[nw]=tp[nw]=0;
    for(int to:E[nw])
    if(to^fa[nw]){
        fa[to]=nw;dep[to]=dep[nw]+1;
        Dfs_1(to);siz[nw]+=siz[to];
        if(siz[to]>siz[son[nw]])son[nw]=to;
    }
}
inline void Dfs_2(int nw){
    if(!tp[nw])tp[nw]=nw;
    if(son[nw]){tp[son[nw]]=tp[nw];Dfs_2(son[nw]);}
    for(int to:E[nw])
    if(to^fa[nw]&&to^son[nw])Dfs_2(to);
}
inline int Lca(int x,int y){
    while(tp[x]^tp[y]){
    if(dep[tp[x]]>=dep[tp[y]])x=fa[tp[x]];
    else y=fa[tp[y]];
    }
    return dep[x]>dep[y]?y:x;
}
inline int Getdis(int x,int y){return dis[x]+dis[y]-2*dis[Lca(x,y)];}

inline void work(){    
    cnt=n=read(),m=read();
    for(int i=1;i<=n;++i)r_E[i].clear();
    for(int i=1,tim=n<<1;i<=tim;++i)E[i].clear();
    for(int i=1;i<=m;++i){
    int u=read(),v=read();
    r_E[u].pb(v),r_E[v].pb(u);
    }
    memset(dfn,0,sizeof(int)*(n+1));
    stp=dfc=0,Tarjan(1);
    dfc=0;Dfs_1(1),Dfs_2(1);
    for(int i=1,Q=read();i<=Q;++i){
    int s=read();rt=0;
    for(int i=1;i<=s;++i)qaq[i]=read();
    sort(qaq+1,qaq+s+1,cmp);
    int ans=0;
    for(int i=1;i<s;++i)ans+=Getdis(qaq[i],qaq[i+1]);
    ans+=Getdis(qaq[s],qaq[1]);
    ans=(ans>>1)+(Lca(qaq[1],qaq[s])<=n)-s;
    printf("%d\n",ans);
    }
}

int main(){
//    freopen("in.in","r",stdin);freopen("cpp.out","w",stdout);
    int T=read();
    while(T--)
    work();
    return 0;
}

[SDOI2018]战略游戏

标签:getc   register   freopen   clear   eof   遍历   tchar   圆点   连通图   

原文地址:https://www.cnblogs.com/fruitea/p/12080380.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!