[HDU6403]:カードゲーム(DFS + DP +ツリーリング)

トピックポータル


タイトル説明

彼女は私が知らない知らない場所にまだあります。
テーブルの上にいくつかのカードを広げ、これは彼女は通常、非常に遊び心のゲームです。Nowカードそれでも、彼女は私と一緒ではありません。無意識のうちに、私はカードをオープンし、その時間中にカードをプレイするためにリコールされました。
各カードの前面と背面の両方が多数、私はテーブルの上にしたいと私はカードを入れるたびに持っており、そのうちのいくつかは、彼女はカードを裏返しに吸引し、最終的にデスクトップ上の数字のすべてを構成します彼らは異なっています。
私は彼女が私は彼女が少なくともフリップの目標数を達成するために必要な最小値、およびそれぞれの目標を達成するために、フリッププログラムの最小数を計算する手伝いをさせて前に突然回以上のことを思い出し、無意識のうちにカードを開いた自分を見ました。
(と2つの方法の同じセットがカードを反転する必要がある場合にのみ場合、2つの方法が同じと考えられている)
私は彼女に送られた溶液プロセス手順書を置けば、彼女は後に、このゲームをプレイする時間は、いくつかのより多くの好みになりますか?


入力形式

各テストポイントのテストデータの複数のセット。
最初の行は、正の整数であり、Tは、データセットの数を表します。
次に、データのセットごとに、最初の行は、正の整数nを、図のテーブル上のカードの数があります。
次いでN、両側をxされた2つの整数X、YのラインカードのY、及び上方Xの現在の数があります。


出力フォーマット

各試験、ライン出力反転の最小数、プログラム番号(モジュロ998244353のプログラム番号)を表す2つの整数のために。
解決策はない場合は「-1 -1。」


サンプル

サンプル入力

3
4
1 2
1 3
4 5
4 6
2
1 1
1 1
3
1 2
3 4
5 6

サンプル出力

2 4
-1 -1
0 1


データ範囲とヒント

データの20%の場合:

の$ n \のleqslant 20 $

$ T = 1 $

データの100%に:

$ n個の\ leqslant {10} ^ 5 $

$ T \ leqslant 50 $

$ 1 \ leqslantのX、Y \ leqslant 2N $


問題の解決策

この質問を参照してください、あなたは、あなたが冷たいです、数学的手法を用いて、溶液を考える必要があります。

だから、それぞれのカードを反転、でもデジタル側カードの裏面に、デジタルフロントを検討するための逆側に相当し、そして私たちの目標は、1の最大の各Unicom社のブロック図内のすべてのポイントの外に作ることです。

しかし、今回は、それは3つのケースに分けられます。

  図1エッジの数がブロック内に存在する場合ポイントユニコムの数よりも大きい場合、その後明らかに満たしていない、直接出力「-1 -1」ボタン。

  2.場合はリンクブロック内にある数字、我々は、それぞれ、時計回り及び反時計実行エッジの数は、ポイントの数に等しい、ブロックリングユニコムツリーで、この高速ユニコム、より少ない数になるように方向を逆にする必要が表示さ両方向の答えが同じ場合は、プログラムの数はそうでない2、および1です。

  図3.エッジの数がブロック同等のポイント-1ユニコムである場合は、China Unicom社は、このブロックが木である、あなたは、統計的DPの答えを使用することを検討しているが、明らかに私たちはツリーのルートの実行のためにすべてのポイントを必要としますしかし、我々はすべての変更のためにも、それとその親側が答える間のルートのみに影響しますが、知恵が答えの1である、またはことが判明 - ;再び、暴力の実行、その後、時間の複雑さは明らかに非常に高いです1、我々は許容時間の範囲で答えを計算することができるように。


コードの時間

#include<bits/stdc++.h>
using namespace std;
struct rec
{
	int nxt;
	int to;
	bool w;//边权为1表示从正面连向反面
}e[200002];
int n;
int head[200001],cnt;
int b,d;
bool vis[200001];
int st,ed,po;
int sta[200001],top;
long long dp[200001],flag[200001];
long long ans,sum,num;
void pre_work()//多测不清空,爆零两行泪TAT……
{
	ans=0;
	sum=cnt=1;
	memset(head,0,sizeof(head));
	memset(vis,0,sizeof(vis));
	memset(dp,0,sizeof(dp));
	memset(flag,0,sizeof(flag));
}
void add(int x,int y,int w)//建边
{
	e[++cnt].nxt=head[x];
	e[cnt].to=y;
	e[cnt].w=w;
	head[x]=cnt;
}
void pre_dfs(int x)//跑每一个联通块,计算点数和边数
{
	d++;
	vis[x]=1;
	for(int i=head[x];i;i=e[i].nxt)
	{
		b++;
		if(!vis[e[i].to])
			pre_dfs(e[i].to);
	}
}
void dfs(int x,int fa)//DP前对每一个联通块进行预处理
{
	flag[x]=0;
	vis[x]=1;
	for(int i=head[x];i;i=e[i].nxt)
	{
		if(e[i].to==fa)continue;
		if(!vis[e[i].to])
		{
			dfs(e[i].to,x);
			flag[x]+=flag[e[i].to]+e[i].w;
		}
		else
		{
			st=x;
			ed=e[i].to;
			po=i;
		}
	}
}
void pro_dfs(int x,int fa)//DP统计答案
{
	sta[++top]=dp[x];
	for(int i=head[x];i;i=e[i].nxt)
		if(e[i].to!=fa&&i!=po&&i!=(po^1))
		{
			dp[e[i].to]=dp[x]+(e[i].w?-1:1);//换根之后的+1或者-1
			pro_dfs(e[i].to,x);
		}
}
int main()
{
	int T;
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d",&n);
		pre_work();
		for(int i=1;i<=n;i++)
		{
			int x,y;
			scanf("%d%d",&x,&y);
			add(x,y,1);
			add(y,x,0);
		}
		for(int i=1;i<=2*n;i++)
			if(!vis[i])
			{
				b=d=0;
				pre_dfs(i);
				if(b/2>d)//判断是否有解
				{
					puts("-1 -1");
					goto nxt;//直接跳到结尾,和continue功能类似,但是可以指定跳转位置
				}
			}
		memset(vis,0,sizeof(vis));
		for(int i=1;i<=2*n;i++)
		{
			if(!vis[i])
			{
				st=ed=po=top=0;
				num=1;
				dfs(i,0);
				dp[i]=flag[i];
				pro_dfs(i,0);
				if(!st)//如果是一棵树
				{
					sort(sta+1,sta+top+1);
					for(int j=2;j<=top;j++)
					{
						if(sta[j]!=sta[1])break;
						num++;
					}
					ans+=sta[1];
				}
				else//是一棵基环树
				{
					POの%= 2; 
					IF(DP [ST] + PO == DP [ED] +(PO ^ 1)。)NUM = 2; 
					他のNUM = 1; 
					ANS + =分(DP [ST] + PO、DP [ED] +(^ PO 1)); 
				} 
				SUM = SUM NUM%998 244 353 *; 
			} 
		} 
		のprintf( "%LLD LLD%\ N-"、ANS、SUM); 
		NXT :; //上記"ジャンプNXT;"にジャンプ位置
	} 
	戻り0; 
}

RP ++

おすすめ

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