卧薪尝胆,厚积薄发。
SDOI2018 战略游戏
Date: Wed Nov 21 07:34:34 CST 2018 In Category: NoCategory

Description:

给一个图,每次询问给出一些关键点,求有多少个点删掉后关键点不再两两联通。
$1\leqslant n\leqslant 10^5$

Solution:

先想如果有两个点怎么做,发现就是圆方树上两个点间这一条链上的圆点个数 $-2$ ,对于有 $k$ 个点的情况也类似,就是他们的虚树上的圆点个数 $-$ 关键点的个数,那么就建出圆方树在上面求虚树即可。

Code:


#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<stack>
#include<cctype>
#include<cstring>
using namespace std;
inline int rd()
{
register int res = 0,f = 1;
register char c = getchar();
while(!isdigit(c))
{
if(c == '-')f = -1;
c = getchar();
}
while(isdigit(c))
{
res = (res << 1) + (res << 3) + c - '0';
c = getchar();
}
return res * f;
}
#define MAXN 200010
int n,m,q;
namespace Tree
{
int tot;
struct edge
{
int to,nxt;
}e[MAXN << 1];
int edgenum = 0;
int lin[MAXN] = {0};
void add(int a,int b)
{
e[++edgenum] = (edge){b,lin[a]};lin[a] = edgenum;
e[++edgenum] = (edge){a,lin[b]};lin[b] = edgenum;
return;
}
int fa[MAXN],dep[MAXN],top[MAXN],son[MAXN],siz[MAXN];
void dfs1(int k,int depth)
{
dep[k] = depth;
siz[k] = 1;
for(int i = lin[k];i != 0;i = e[i].nxt)
{
if(e[i].to != fa[k])
{
fa[e[i].to] = k;
dfs1(e[i].to,depth + 1);
siz[k] += siz[e[i].to];
if(son[k] == 0 || siz[e[i].to] > siz[son[k]])
{
son[k] = e[i].to;
}
}
}
return;
}
void dfs2(int k,int tp)
{
top[k] = tp;
if(son[k] == 0)return;
dfs2(son[k],tp);
for(int i = lin[k];i != 0;i = e[i].nxt)
{
if(e[i].to != fa[k] && e[i].to != son[k])
{
dfs2(e[i].to,e[i].to);
}
}
return;
}
inline int LCA(int a,int b)
{
while(top[a] != top[b])
{
if(dep[top[a]] < dep[top[b]])swap(a,b);
a = fa[top[a]];
}
return (dep[a] < dep[b] ? a : b);
}
int val[MAXN],res[MAXN];
int dfn[MAXN],cnt = 0;
void dfs(int k)
{
res[k] += val[k];
dfn[k] = ++cnt;
for(int i = lin[k];i != 0;i = e[i].nxt)
{
if(dfn[e[i].to] != 0)continue;
res[e[i].to] += res[k];
dfs(e[i].to);
}
return;
}
void init()
{
cnt = 0;
memset(dfn,0,sizeof(dfn));
dfs(1);
dfs1(1,1);dfs2(1,1);
return;
}
bool cmp_dfn(int a,int b){return dfn[a] < dfn[b];}
int s[MAXN],tp;
int v[MAXN];
void calc()
{
scanf("%d",&v[0]);
for(int i = 1;i <= v[0];++i)v[i] = rd();
sort(v + 1,v + 1 + v[0],cmp_dfn);
tp = 0;s[++tp] = v[1];
int ans = 0;
for(int i = 2;i <= v[0];++i)
{
int lca = LCA(v[i],s[tp]);
if(lca == s[tp])
{
s[++tp] = v[i];
continue;
}
while(tp >= 2)
{
if(dfn[lca] < dfn[s[tp - 1]])
{
ans += res[s[tp]] - res[s[tp - 1]];
--tp;
}
else
{
ans += res[s[tp]] - res[lca];
--tp;
break;
}
}
if(tp == 1 && dfn[lca] < dfn[s[tp]])
{
ans += res[s[tp]] - res[lca];
s[tp] = lca;
}
if(lca != s[tp])s[++tp] = lca;
s[++tp] = v[i];
}
while(tp >= 2)
{
ans += res[s[tp]] - res[s[tp - 1]];
--tp;
}
ans += val[s[tp]];
printf("%d\n",ans - v[0]);
return;
}
}
namespace Graph
{
struct edge
{
int to,nxt;
}e[MAXN << 1];
int edgenum = 0;
int lin[MAXN] = {0};
void add(int a,int b)
{
e[++edgenum] = (edge){b,lin[a]};lin[a] = edgenum;
e[++edgenum] = (edge){a,lin[b]};lin[b] = edgenum;
return;
}
int dfn[MAXN],low[MAXN],tot = 0;
stack<int> s;
void tarjan(int k)
{
dfn[k] = low[k] = ++tot;
Tree::val[k] = 1;
s.push(k);
for(int i = lin[k];i != 0;i = e[i].nxt)
{
if(dfn[e[i].to] == 0)
{
tarjan(e[i].to);
low[k] = min(low[k],low[e[i].to]);
if(low[e[i].to] >= dfn[k])
{
int t;
++Tree::tot;
do
{
t = s.top();s.pop();
Tree::add(Tree::tot,t);
}while(t != e[i].to);
Tree::add(Tree::tot,k);
}
}
else
{
low[k] = min(low[k],dfn[e[i].to]);
}
}
return;
}
void init()
{
Tree::edgenum = 0;
memset(Tree::val,0,sizeof(Tree::val));
memset(Tree::res,0,sizeof(Tree::res));
memset(Tree::fa,0,sizeof(Tree::fa));
memset(Tree::lin,0,sizeof(Tree::lin));
memset(Tree::son,0,sizeof(Tree::son));
edgenum = 0;
memset(lin,0,sizeof(lin));
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
tot = 0;
while(!s.empty())s.pop();
return;
}
void work()
{
Tree::tot = n;
tarjan(1);
Tree::init();
return;
}
}
void work()
{
scanf("%d%d",&n,&m);
Graph::init();
for(int i = 1;i <= m;++i)Graph::add(rd(),rd());
Graph::work();
scanf("%d",&q);
while(q--)Tree::calc();
return;
}
int main()
{
int testcases = rd();
while(testcases--)work();
return 0;
}
Copyright © 2020 wjh15101051
ღゝ◡╹)ノ♡