LuoguP4606 [SDOI2018]战略游戏
题目描述
题目描述
省选临近,放飞自我的小 QQ 无心刷题,于是怂恿小 CC 和他一起颓废,玩起了一款战略游戏。
这款战略游戏的地图由 nn 个城市以及 mm 条连接这些城市的双向道路构成,并且从任意一个城市出发总能沿着道路走到任意其他城市。
现在小 CC 已经占领了其中至少两个城市,小 QQ 可以摧毁一个小 CC 没占领的城市,同时摧毁所有连接这个城市的道路。只要在摧毁这个城市之后能够找到某两个小 CC 占领的城市 uu 和 vv,使得从 uu 出发沿着道路无论如何都不能走到 vv,那么小 QQ 就能赢下这一局游戏。
小 QQ 和小 CC 一共进行了 qq 局游戏,每一局游戏会给出小 CC 占领的城市集合 SS,你需要帮小 QQ 数出有多少个城市在他摧毁之后能够让他赢下这一局游戏。
Solution
题意:给定一个nnn个点mmm条边的无向图,QQQ组询问,每次给出一个点集{S}\{S\}{S},询问有多少点被删掉之后,点集{S}\{S\}{S}会存在两点不连通。
建广义圆方树。
建出圆方树之后,让两个点u,vu,vu,v不连通相当于删掉了除了u,vu,vu,v以外的某一个u−>vu->vu−>v路径上的圆点。
设点集{S}={a1,a2,...,ak}\{S\}=\{a_1,a_2,...,a_k\}{S}={a1,a2,...,ak}
点集中的点按dfsdfsdfs序排序,
令相邻点对(包括(a1,ak)(a_1,a_k)(a1,ak))之间路径的圆点数量和为ansansans。
总的答案即为ans/2−k+[LCA(a1,ak)为圆点]ans/2-k+[LCA(a_1,a_k)为圆点]ans/2−k+[LCA(a1,ak)为圆点]。
时间复杂度O(nlgn)O(n\lg n)O(nlgn)。
这题也可以用虚树做,其实和上述方法差不多,只是实现方式不同。
#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <string>
#include <cstring>
#include <ctime>
#include <cassert>
#include <string.h>
//#include <unordered_set>
//#include <unordered_map>
//#include <bits/stdc++.h>#define MP(A,B) make_pair(A,B)
#define PB(A) push_back(A)
#define SIZE(A) ((int)A.size())
#define LEN(A) ((int)A.length())
#define FOR(i,a,b) for(int i=(a);i<(b);++i)
#define fi first
#define se secondusing namespace std;template<typename T>inline bool upmin(T &x,T y) { return y<x?x=y,1:0; }
template<typename T>inline bool upmax(T &x,T y) { return x<y?x=y,1:0; }typedef long long ll;
typedef unsigned long long ull;
typedef long double lod;
typedef pair<int,int> PR;
typedef vector<int> VI;const lod eps=1e-11;
const lod pi=acos(-1);
const int oo=1<<30;
const ll loo=1ll<<62;
const int mods=1e9+7;
const int MAXN=200005;
const int INF=0x3f3f3f3f;//1061109567
/*--------------------------------------------------------------------*/
inline int read()
{int f=1,x=0; char c=getchar();while (c<'0'||c>'9') { if (c=='-') f=-1; c=getchar(); }while (c>='0'&&c<='9') { x=(x<<3)+(x<<1)+(c^48); c=getchar(); }return x*f;
}
vector<int> e[MAXN],E[MAXN];
int n,m,dep[MAXN],Log[MAXN],a[MAXN];
int dfn[MAXN],low[MAXN],stk[MAXN],fa[MAXN][20],dist[MAXN],bnum=0,top=0,DFN=0;
int compare(int x,int y) { return dfn[x]<dfn[y]; }
void add_edge(int u,int v){ /*cout<<"Edge:"<<u<<" "<<v<<endl;*/ E[u].PB(v),E[v].PB(u); }
void tarjan(int x,int father)
{dfn[x]=low[x]=++DFN;stk[++top]=x;for (auto v:e[x]){if (v==father) continue;if (dfn[v]) { low[x]=min(low[x],dfn[v]); continue; }tarjan(v,x);low[x]=min(low[x],low[v]);if (low[v]>=dfn[x]){int y;bnum++;add_edge(n+bnum,x);while (y=stk[top--]){add_edge(n+bnum,y);if (v==y) break;}}}
}
void dfs(int x,int father)
{
// cout<<x<<" "<<father<<endl;dfn[x]=++DFN;dep[x]=dep[father]+1;fa[x][0]=father;for (int i=1;i<=Log[dep[x]];i++) fa[x][i]=fa[fa[x][i-1]][i-1];for (auto v:E[x])if (v!=father) dist[v]=dist[x]+(v<=n),dfs(v,x);
}
int get_LCA(int x,int y)
{if (dep[x]<dep[y]) swap(x,y);for (int i=Log[dep[x]];i>=0;i--)if (dep[fa[x][i]]>=dep[y]) x=fa[x][i];if (x==y) return x;for (int i=Log[dep[x]];i>=0;i--)if (fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];return fa[x][0];
}
int getans(int x,int y)
{int lca=get_LCA(x,y);
// cout<<x<<":"<<dist[x]<<" "<<y<<":"<<dist[y]<<" "<<lca<<":"<<dist[lca]<<endl;
// cout<<dep[x]<<" "<<dep[y]<<" "<<dep[lca]<<endl; return dist[x]+dist[y]-dist[lca]*2;
}
void Init()
{bnum=0,top=0,DFN=0;memset(dist,0,sizeof dist);memset(dfn,0,sizeof dfn);memset(fa,0,sizeof fa);memset(low,0,sizeof low);for (int i=1;i<=n*2;i++) e[i].clear(),E[i].clear();
}
int main()
{
// freopen("c.in","r",stdin); n=read(),m=read();int q=read();Init();for (int i=1;i<=m;i++){int u=read(),v=read();e[u].PB(v);e[v].PB(u);}tarjan(1,0);dep[0]=-1,Log[1]=0,dist[1]=0;for (int i=2;i<=n*2;i++) Log[i]=Log[i>>1]+1; DFN=0;dfs(1,0);while (q--){int k=2;a[1]=read(),a[2]=read();sort(a+1,a+k+1,compare);int ans=getans(a[1],a[k]);for (int i=1;i<k;i++) ans+=getans(a[i],a[i+1]);
// cout<<"Ans:"<<ans<<endl;printf("%d\n",ans/2-k+(get_LCA(a[1],a[k])<=n));}return 0;
}