[BZOJ5329] [SDOI2018] 战略游戏
题目链接
BZOJ.
LOJ.
Solution
对原图建立圆方树。
那么可以注意到只有圆点会被算到答案,对于一个圆点,合法当且仅当去掉这个点之后的若干个连通块有两个或以上有标记点。
那么可以对标记点在圆方树上建立虚树,那么虚树边上的圆点和那些作为\(LCA\)建上去的圆点的个数即为答案。
注意建虚树时把\(1\)号点建进去的话要特判\(1\)号点和所连的边选不选。
这题是真的难码...
#include<bits/stdc++.h>
using namespace std;
void read(int &x) {
x=0;int f=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f;
}
void print(int x) {
if(x<0) putchar('-'),x=-x;
if(!x) return ;print(x/10),putchar(x%10+48);
}
void write(int x) {if(!x) putchar('0');else print(x);putchar('\n');}
#define lf double
#define ll long long
const int maxn = 5e5+10;
const int inf = 1e9;
const lf eps = 1e-8;
int n,m,cnt,sz[maxn],dfn[maxn],dis[maxn],dfncnt;
struct Tree {
int head[maxn],tot,dep[maxn],fa[maxn][20];
struct edge{int to,nxt;}e[maxn<<1];
void add(int u,int v) {e[++tot]=(edge){v,head[u]},head[u]=tot;}
void ins(int u,int v) {add(u,v),add(v,u);}
void dfs(int x,int Fa) {
dis[x]=dis[Fa]+(x<=n),dep[x]=dep[Fa]+1,fa[x][0]=Fa,dfn[x]=++dfncnt,sz[x]=1;
for(int i=1;i<=18;i++) fa[x][i]=fa[fa[x][i-1]][i-1];
for(int i=head[x];i;i=e[i].nxt)
if(e[i].to!=Fa) dfs(e[i].to,x),sz[x]+=sz[e[i].to];
}
int lca(int x,int y) {
if(dep[x]<dep[y]) swap(x,y);
for(int i=18;~i;i--) if(dep[fa[x][i]]>=dep[y]) x=fa[x][i];
if(x==y) return x;
for(int i=18;~i;i--) if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
void clear() {
for(int i=1;i<=cnt;i++) sz[i]=dfn[i]=dis[i]=head[i]=dep[i]=0;
tot=dfncnt=0;
}
}T;
struct Graph {
int head[maxn],tot,Dfn[maxn],low[maxn],dfn_cnt,sta[maxn],top,vis[maxn];
struct edge{int to,nxt;}e[maxn<<1];
void add(int u,int v) {e[++tot]=(edge){v,head[u]},head[u]=tot;}
void ins(int u,int v) {add(u,v),add(v,u);}
void clear() {
for(int i=1;i<=cnt;i++) head[i]=Dfn[i]=low[i]=sta[i]=vis[i]=0;
dfn_cnt=tot=top=0;
}
void tarjan(int x,int fa) {
low[x]=Dfn[x]=++dfn_cnt;sta[++top]=x;vis[x]=1;
for(int v,i=head[x];i;i=e[i].nxt) {
if((v=e[i].to)==fa) 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]) {
++cnt;
while(top) {
int now=sta[top--];
T.ins(now,cnt);
if(now==v) break;
}T.ins(cnt,x);
}
}
}
void prepare() {
for(int i=1,x,y;i<=m;i++) read(x),read(y),ins(x,y);
cnt=n,tarjan(1,0),T.dfs(1,0);
}
}G;
int cmp(int x,int y) {return dfn[x]<dfn[y];}
struct Virtual_Tree {
int head[maxn],tot,ans,use[maxn],used,sta[maxn],top,in[maxn],k,vis[maxn],op;
struct edge{int to,nxt,w;}e[maxn<<1];
void add(int u,int v,int w) {e[++tot]=(edge){v,head[u],w},head[u]=tot;}
void ins(int u,int v,int w) {add(u,v,w),add(v,u,w);}
void build() {
sort(in+1,in+k+1,cmp);sta[++top]=1;
for(int i=1;i<=k;i++) {
if(in[i]==1) continue;
int t=T.lca(sta[top],in[i]),pre=-1;
while(dfn[sta[top]]>dfn[t]&&dfn[sta[top]]<dfn[t]+sz[t]) {
if(pre!=-1) ins(pre,sta[top],dis[pre]-dis[sta[top]]-(pre<=n));
use[++used]=pre=sta[top];top--;
}
if(pre!=-1) ins(pre,t,dis[pre]-dis[t]-(pre<=n));
if(sta[top]!=t) sta[++top]=t;
sta[++top]=in[i];
}
int pre=-1;
while(top) {
if(pre!=-1) ins(pre,sta[top],dis[pre]-dis[sta[top]]-(pre<=n));
use[++used]=pre=sta[top];top--;
}
}
void clear() {
for(int i=1;i<=used;i++) head[use[i]]=vis[use[i]]=0;
top=used=tot=0;
}
void dfs(int x,int fa) {
if(!vis[x]) ans+=x<=n;
for(int i=head[x];i;i=e[i].nxt)
if(e[i].to!=fa) dfs(e[i].to,x),ans+=e[i].w;
}
void solve() {
read(k);for(int i=1;i<=k;i++) read(in[i]),vis[in[i]]=1;
build();op=0;for(int i=head[1];i;i=e[i].nxt) op++;
ans=0;dfs(1,0);
if(op==1&&in[1]!=1) {
ans--;
for(int i=head[1];i;i=e[i].nxt) ans-=e[i].w;
}
write(ans);clear();
}
}VT;
void solve() {
read(n),read(m),G.prepare();
int q;read(q);for(int i=1;i<=q;i++) VT.solve();
}
int main() {
int t;read(t);while(t--) solve(),G.clear(),T.clear();
return 0;
}