2 条题解

  • 0
    @ 2026-6-8 21:59:56

    题目要求摧毁一个点使得两个关键点不连通,显然摧毁的点只能是割点。所以我们先求出割点并且建立出原图的圆方树。

    那么圆方树是什么呢?我们把原图的每个点看作圆点,给每个点双(BCC)新建一个方点,然后让每个圆点向对应的点双方点建边。显然,新图的边只会连接圆点和方点,所以是一棵树。并且只有割点会同时连向若干个方点,因为只有割点同时属于多个点双。

    现在我们考虑哪些点可以摧毁。对于一个点 xx,如果它可以摧毁,只可能是它的子树和外子树都有关键点(一类),或者它的多个子节点的子树内都有关键点(二类),并且它需要是割点。

    这样我们可以先预处理出一个 sumsum 数组,sumxsum_x 表示从树根到 xx 节点之间割点的个数,是一个前缀和。

    然后每组询问建出虚树,sizxsiz_x 表示 xx 子树内关键点个数。首先考虑一类,如果 sizx>0siz_x>0 并且 msizx>0m-siz_x>0,也就是内外子树都存在关键点,那么 xxprepre 之间的割点都可以被摧毁,产生的贡献就是 sumxsumpresum_x-sum_{pre},当然,如果 xx 也是关键点,贡献要减去一。接下来考虑二类,我们可以记录一个 cntcnt,表示有多少棵子节点的子树内有关键点。如果 cnt>2cnt>2 就说明可以摧毁 xx 点,但依然要判断 xx 不是关键点并且 xx 是割点。

    下面是 AC 代码。

    #include <bits/stdc++.h>
    using namespace std;
    
    #define int long long
    
    const int INF = 0x7f7f7f7f;
    
    int Q,n,m,q,ans=0,a[200005],ls[200005];
    int ee,h[200005],nex[200005<<1],to[200005<<1];
    int low[200005],bcc,cut[200005];
    int dep[200005],fa[200005][20],dfn[200005],sum[200005];
    int top,sk[200005],siz[200005];
    vector<int> c[200005];
    
    void addedge(int x,int y)
    {
    	nex[++ee] = h[x], to[ee] = y, h[x] = ee;
    }
    
    void tarjin(int x,int fa)
    {
    	int ch=0;
    	dfn[x] = low[x] = ++dfn[0];
    	for(int i=h[x];i;i=nex[i])
    	{
    		if(dfn[to[i]]==0)
    		{
    			sk[++top] = to[i];
    			tarjin(to[i],x);
    			low[x] = min(low[x],low[to[i]]);
    			if(x==fa)
    				ch++;
    			else if(low[to[i]]>=dfn[x])
    				cut[x] = 1;
    			if(low[to[i]]>=dfn[x])
    			{
    				bcc++;
    				while(sk[top]!=to[i])
    					c[bcc].push_back(sk[top]), top--;
    				c[bcc].push_back(sk[top]), top--;
    				c[bcc].push_back(x);
    			}
    		}
    		else if(to[i]!=fa)
    			low[x] = min(low[x],dfn[to[i]]);
    	}
    	if(x==fa && ch>=2)
    		cut[x] = 1;
    }
    
    void dfs0(int x,int pre)
    {
    	dep[x] = dep[pre]+1, fa[x][0] = pre, dfn[x] = ++dfn[0], sum[x] = sum[pre]+cut[x];
    	for(int j=1;j<20;j++)
    		fa[x][j] = fa[fa[x][j-1]][j-1];
    	for(int i=h[x];i;i=nex[i])
    		if(to[i]!=pre)
    			dfs0(to[i],x);
    }
    
    int lca(int x,int y)
    {
    	if(dep[x]<dep[y]) swap(x,y);
    	while(dep[x]>dep[y])
    		x = fa[x][(int)log2(dep[x]-dep[y])];
    	if(x==y)
    		return y;
    	for(int k=19;k>=0;k--)
    		if(fa[x][k]!=fa[y][k])
    			x = fa[x][k], y = fa[y][k];
    	return fa[x][0];
    }
    
    bool cmp(int x,int y)
    {
    	return dfn[x]<dfn[y];
    }
    
    void build()
    {
    	ee = top = 0;
    	sort(ls+1,ls+1+m,cmp);
    	if(ls[1]!=1) sk[++top] = 1;
    	for(int i=1;i<=m;i++)
    	{
    		if(top==0)
    		{
    			sk[++top] = ls[i];
    			continue;
    		}
    		int z=lca(sk[top],ls[i]);
    		while(top>1 && dep[z]<dep[sk[top-1]])
    			addedge(sk[top],sk[top-1]), addedge(sk[top-1],sk[top]), top--;
    		if(dep[z]<dep[sk[top]])
    			addedge(sk[top],z), addedge(z,sk[top]), top--;
    		if(top==0 || sk[top]!=z)
    			sk[++top] = z;
    		sk[++top] = ls[i];
    	}
    	while(--top)
    		addedge(sk[top],sk[top+1]), addedge(sk[top+1],sk[top]);
    }
    
    void dfs1(int x,int pre)
    {
    	siz[x] = a[x];
    	for(int i=h[x];i;i=nex[i])
    		if(to[i]!=pre)
    			dfs1(to[i],x), siz[x] += siz[to[i]];
    }
    
    void dfs(int x,int pre)
    {
    	int cnt=0;
    	if(a[x]==1)
    		siz[x] = 1;
    	else
    		siz[x] = 0;
    	for(int i=h[x];i;i=nex[i])
    		if(to[i]!=pre)
    		{
    			dfs(to[i],x);
    			if(siz[to[i]]>0)
    				cnt++;
    			siz[x] += siz[to[i]];
    		}
    	if(m-siz[x]>=1)
    	{
    		if(!a[x])
    			ans += sum[x]-sum[pre];
    		else
    			ans += max(sum[fa[x][0]]-sum[pre],0ll);
    	}
    	else if(cnt>=2 && !a[x])
    		ans += (x<=n);
    	a[x] = h[x] = 0;
    }
    
    signed main()
    {
    	cin>>Q;
    	while(Q--)
    	{
    		memset(h,0,sizeof(h)), ee = 0;
    		memset(dfn,0,sizeof(dfn)), memset(cut,0,sizeof(cut));
    		for(int i=1;i<=bcc;i++)
    			c[i].clear();
    		bcc = 0;
    		memset(sum,0,sizeof(sum));
    		cin>>n>>m;
    		for(int i=1,x,y;i<=m&&scanf("%lld%lld",&x,&y);i++)
    			addedge(x,y), addedge(y,x);
    		for(int i=1;i<=n;i++)
    			if(dfn[i]==0)
    				tarjin(1,1);
    		ee = 0, memset(h,0,sizeof(h));
    		for(int i=1;i<=bcc;i++)
    			for(int j=0;j<c[i].size();j++)
    				addedge(c[i][j],i+n), addedge(i+n,c[i][j]);
    		memset(dfn,0,sizeof(dfn));
    		dfs0(1,0);
    		ee = 0, memset(h,0,sizeof(h));
    		cin>>q;
    		while(q--)
    		{
    			scanf("%lld",&m);
    			for(int i=1;i<=m&&scanf("%lld",ls+i);i++)
    				a[ls[i]]++;
    			build();
    			ans = 0;
    			dfs(1,0);
    			printf("%lld\n",ans);
    		}
    	}
    	
    	return 0;
    }
    

    祝大家 AC 愉快!

    • 0
      @ 2025-10-8 17:14:48
      #include<bits/stdc++.h>
      using namespace std;
      const int N=1e5+10;vector<int>G1[N],G2[N<<1]; 
      int n,tsp,cnt,dfn[N<<1],low[N],a[N];
      stack<int>stk;
      void tarjan(int x)
      {
      	dfn[x]=low[x]=++tsp;
      	stk.push(x);
      	for(int y:G1[x])
      	{
      		if(!dfn[y])
      		{
      			tarjan(y);
      			low[x]=min(low[x],low[y]);
      			if(dfn[x]==low[y])
      			{
      				cnt++;G2[x].push_back(cnt);
      				for(int z=-1;z!=y;)
      				{
      					z=stk.top();stk.pop();
      					G2[cnt].push_back(z);
      				}
      			}
      		}
      		else low[x]=min(low[x],dfn[y]);
      	}
      }
      int D,dep[N<<1],d[N<<1],st[N<<1][18];
      void dfs(int x,int xfa)
      {
      	dfn[x]=++tsp;dep[x]=dep[xfa]+1;
      	st[x][0]=xfa;for(int i=1;i<=D;i++)st[x][i]=st[st[x][i-1]][i-1];
      	for(int y:G2[x])
      	{
      		d[y]=d[x]+(y<=n);dfs(y,x);
      	}
      }
      int LCA(int x,int y)
      {
      	if(dep[x]<dep[y])swap(x,y);
      	for(int i=D;i>=0;i--)if(dep[st[x][i]]>=dep[y])x=st[x][i];
      	if(x==y) return x;
      	for(int i=D;i>=0;i--)if(st[x][i]!=st[y][i])x=st[x][i],y=st[y][i];
      	return st[x][0];
      }
      int main()
      {
      	int T;scanf("%d",&T);
      	while(T--)
      	{
      		int m;scanf("%d%d",&n,&m);
      		memset(G1,0,sizeof(G1));
      		for(int i=1,x,y;i<=m;i++)
      		{
      			scanf("%d%d",&x,&y);
      			G1[x].push_back(y);G1[y].push_back(x);
      		}
      		cnt=n;
      		memset(G2,0,sizeof(G2));
      		tsp=0;memset(dfn,0,sizeof(dfn));memset(low,0,sizeof(low));
      		tarjan(1),stk.pop();
      		tsp=0;D=log2(cnt);
              memset(dep,0,sizeof(dep));memset(st,0,sizeof(st));memset(d,0,sizeof(d));
              dfs(1,0);
      		int q;scanf("%d",&q);
      		while(q--)
      		{
      			int k;scanf("%d",&k);
      			for(int i=1;i<=k;i++)scanf("%d",&a[i]);
      			sort(a+1,a+k+1,[](int x,int y){ return dfn[x]<dfn[y];});
      			int ans=0;
      			for(int i=1,x,y;i<=k;i++)
      			{	x=a[i];y=a[i%k+1];
      				ans+=d[x]+d[y]-2*d[LCA(x,y)];
      			}
      			if(LCA(a[k],a[1])<=n)ans+=2;
      			printf("%d\n",ans/2-k);
      		}
      	}
      	return 0;
      }
      
      • 1

      D31_3【圆方树】[SDOI2018] 战略游戏

      信息

      ID
      2371
      时间
      10000ms
      内存
      512MiB
      难度
      8
      标签
      递交数
      107
      已通过
      14
      上传者