[NOIPのシミュレーションテスト]:ホーム(尖塔)

トピックポータル(内部質問7)


入力形式

最初の行の整数T、Tは、全データセットを表します。
各試験のために、2つの数の最初の行は、n、mはn型建物、M道路を表します。
二つの整数、U、Vの次のm行、uおよびv番目の建物との間に接続された建造物を表します。


出力フォーマット

各試験のために、2本の出力線の合計。
最初の行は、xの合計数xが(Nドットを含む1点としない)点を通過しなければならない表します。
これは、2つの点を通過しなければならない次の行番号x、xは、それらの点です。出力に昇順。
注意:出力0の最初の行は、その後、第2行、次のものの出力は、あなたが唯一の改行を持っている必要があります。


サンプル

サンプル入力1:

1
4 3
1 2
2 3
3 4

出力例1:

2
2 3

サンプル入力2:

1
5
1 2
2 3
3 4
4
4 1

サンプル出力2:

1
4


データ範囲とヒント

$ T \は10 $をleqslant。

leqslant20万$ \ $ N。

複数のエッジまたはループバックがあるかもしれません。$ M \ leqslant n $という。


問題の解決策

あるすべての最初の、この質問には、我々は尖塔ポイント還元が不可欠であり、カットポイントを期待するべきです。

しかし、必要なポイントは、ポイントを切断しなければならないが、丸い側ツリー上の点のみ1本鎖のNには、必要なポイントであるため、必ずしもポイントカットは、必要点です。

だから我々はそれがどのようにすればよいですか?

明らかにポイントは、新しいマップ上で実行されている、尖塔、構築されたマップを縮小することができますバック見つけ、あまりにも複雑で、より大きな定数を実現しました。

したがって、我々は別のアプローチを検討してください。

タイムスタンプは、その子ノードの少ないバックトラックポイント値である場合には尖塔のアルゴリズムでは、それはカットポイントでなければなりません。

息子のポイントのタイムスタンプは、タイムスタンプがnに等しいよりも小さいのであれば、それはnにパス1上の点を通過しなければなりません。

你可能会仍给我类似下面这张图:

 

如果我们先访问了点2,那么点3和点4的时间戳都比点5小,怎么办呢?

访问了点2,如果我们先访问了点3,那么点4还没有被访问过,而点4恰恰是判定点2为割点的条件,而此时点2连割点的条件都没有满足,更不用考虑是不是必经点了。

如果我们先访问了点4,那么点5还没有被访问到,所以也不成立。


代码时刻

#include<bits/stdc++.h>
using namespace std;
struct rec
{
	int nxt;
	int to;
}e[1000000];
int head[200001],cnt;
int n,m;
int dfn[200001],low[200001],tot;
bool cut[200001];
int ans;
void pre_work()
{
	cnt=tot=ans=0;
	for(int i=1;i<=n;i++)
		head[i]=dfn[i]=low[i]=cut[i]=0;
}
void add(int x,int y)
{
	e[++cnt].nxt=head[x];
	e[cnt].to=y;
	head[x]=cnt;
}
void tarjan(int x)
{
	dfn[x]=low[x]=++tot;
	int flag=0;
	for(int i=head[x];i;i=e[i].nxt)
	{
		if(!dfn[e[i].to])
		{
			tarjan(e[i].to);
			low[x]=min(low[x],low[e[i].to]);
			if(dfn[x]<=low[e[i].to]&&dfn[e[i].to]<=dfn[n])//加一个判定条件
			{
				flag++;
				if(x!=1||flag>1){ans++;cut[x]=1;}
			}
		}
		else low[x]=min(low[x],dfn[e[i].to]);
	}
}
int main()
{
	int T;
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d%d",&n,&m);
		pre_work();
		for(int i=1;i<=m;i++)
		{
			int u,v;
			scanf("%d%d",&u,&v);
			add(u,v);
			add(v,u);
		}
		tarjan(1);
		printf("%d\n",ans);
		for(int i=1;i<=n;i++)
			if(cut[i])printf("%d ",i);
		puts("");
	}
	return 0;
}

不管你怎么说,反正我觉得这个代码比其他人的代码简洁多了~


rp++

おすすめ

転載: www.cnblogs.com/wzc521/p/11248404.html