• [SDOI2018]战略游戏


    题目

    圆方树其实并没有那么难

    圆方树的构建比较简单,就是一个tarjan把点双跑出来,对于每一个点双我们多建一个方点,把原图中的点称为圆点,将点双内所有圆点向方点连边,之后我们就得到了原图的圆方树

    关于圆方树的性质,zyb大爷在他的题解里写了很多,这里就不再抄一遍了

    至于这道题,就是把圆点拿出来建棵虚树,割虚树上的圆点就会使得点集不连通了

    那么如何求虚树中的圆点个数呢,我们可以将圆点的点权放到其连向父亲的那条边上去,这样统计虚树的边权和就好了;特殊的,当虚树的最高点为圆点时,它连向父亲的边不在虚树里,所以需要额外计算

    代码,就是tarjan板子和虚树板子,写起来爽就是了

    代码

    #include<bits/stdc++.h>
    #define re register
    #define min(a,b) ((a)<(b)?(a):(b))
    const int maxn=2e5+7;
    inline int read() {
    	char c=getchar();int x=0;while(c<'0'||c>'9') c=getchar();
    	while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
    }
    struct E{int v,nxt;}e[maxn<<1];
    int n,m,num,pre[maxn],head[maxn],sum[maxn],cnt,pt[maxn],Top[maxn];
    int st[maxn],son[maxn],dfn[maxn],ans,dep[maxn],top,fa[maxn],rn;
    inline int cmp(int A,int B) {return dfn[A]<dfn[B];}
    inline void add(int x,int y) {
    	e[++num].v=y;e[num].nxt=head[x];head[x]=num;
    	e[++num].v=x;e[num].nxt=head[y];head[y]=num;
    }
    void dfs1(int x) {
    	sum[x]=1;son[x]=0;Top[x]=0;
    	for(re int i=head[x];i;i=e[i].nxt) {
    		if(dep[e[i].v]) continue;
    		dep[e[i].v]=dep[x]+1;fa[e[i].v]=x;
    		pre[e[i].v]=pre[x]+(e[i].v<=rn);
    		dfs1(e[i].v);sum[x]+=sum[e[i].v];
    		if(sum[e[i].v]>sum[son[x]]) son[x]=e[i].v;
    	}
    }
    void dfs2(int x,int topf) {
    	dfn[x]=++cnt;Top[x]=topf;
    	if(!son[x]) return;dfs2(son[x],topf);
    	for(re int i=head[x];i;i=e[i].nxt) if(!Top[e[i].v]) dfs2(e[i].v,e[i].v);
    }
    inline int LCA(int x,int y) {
    	while(Top[x]!=Top[y]) {
    		if(dep[Top[x]]<dep[Top[y]]) y=fa[Top[y]];
    		else x=fa[Top[x]];
    	}
    	return dep[x]<dep[y]?x:y;
    }
    inline void ins(int x) {
    	if(top<1) {st[++top]=x;return;}
    	int lca=LCA(st[top],x);
    	if(lca==st[top]) {st[++top]=x;return;}
    	while(top>1&&dfn[st[top-1]]>=dfn[lca])
    		ans+=pre[st[top]]-pre[st[top-1]],--top;
    	if(lca!=st[top]) ans+=pre[st[top]]-pre[lca],st[top]=lca;
    	st[++top]=x;
    }
    namespace Build {
    	struct E{int v,nxt;}e[maxn<<1];
    	int cnt,top,num;
    	int head[maxn>>1],dfn[maxn>>1],low[maxn>>1],st[maxn>>1];
    	inline void add_(int x,int y) {
    		e[++num].v=y;e[num].nxt=head[x];head[x]=num;
    		e[++num].v=x;e[num].nxt=head[y];head[y]=num;
    	}
    	void tarjan(int x,int fa) {
    		dfn[x]=low[x]=++cnt;
    		for(re int i=head[x];i;i=e[i].nxt)
    			if(!dfn[e[i].v]) {
    				st[++top]=e[i].v;tarjan(e[i].v,x);low[x]=min(low[x],low[e[i].v]);
    				if(low[e[i].v]>=dfn[x]) {
    					++n;do{add(st[top],n);}while(st[top--]!=e[i].v);
    					add(x,n);
    				}
    			}else if(x!=fa) low[x]=min(low[x],dfn[e[i].v]);
    	}
    	inline void solve() {
    		cnt=top=num=0;memset(head,0,sizeof(head));memset(st,0,sizeof(st));
    		memset(dfn,0,sizeof(dfn));memset(low,0,sizeof(low));
    		for(re int i=1;i<=m;i++) add_(read(),read());
    		tarjan(1,0);
    	}
    }
    int main() {
    	for(re int T=read();T;--T) {
    		rn=n=read(),m=read();num=0;
    		memset(head,0,sizeof(head));memset(dep,0,sizeof(dep));
    		Build::solve();cnt=0;dep[1]=1,dfs1(1);dfs2(1,1);
    		for(re int sz,Q=read();Q;--Q) {
    			sz=read();top=0;ans=0;
    			for(re int i=1;i<=sz;i++) pt[i]=read();
    			std::sort(pt+1,pt+sz+1,cmp);
    			for(re int i=1;i<=sz;i++) ins(pt[i]);
    			while(top>1) ans+=pre[st[top]]-pre[st[top-1]],--top;
    			printf("%d
    ",ans+(st[1]<=rn)-sz);
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    C++之Static与Const
    LInux主机与虚拟机网络链接
    C#数据类型与数据类型转化
    C#网编Console(二)
    C#网编Winform(三)
    C#网编基础类与API(一)
    C实现CPU大小端判断
    QT程序图标设置
    四、初识Socket套接字API
    C++之继承(二)
  • 原文地址:https://www.cnblogs.com/asuldb/p/12023155.html
Copyright © 2020-2023  润新知