[HDU6403] : 카드 게임 (DFS + DP + 트리 링)

주제 포털


제목 설명

그녀는 내가 모르는 모르는 장소에 아직도있다.
테이블에 어떤 카드를 확산이 그녀는 일반적으로 매우 장난 게임이다. Now 카드 여전히 그녀는 나와 함께하지 않습니다. 무의식적으로, 내가 카드를 열어, 그 시간 동안 카드를 재생 회상했다.
각 카드의 앞면과 뒷면 모두 숫자, 나는 테이블에 원하는대로 내가 카드를 넣을 때마다이 있고, 그 중 일부는 그녀는 카드를 뒤집 빨려, 그리고 마지막으로 바탕 화면에있는 모든 숫자를 구성하는 그들은 다르다.
자신도 모르게 갑자기 번 이상 기억 카드를, 열에서 그녀는 내가 그녀가 자신의 목표를 달성하기 위해 적어도 플립의 대상 번호, 플립 프로그램의 최소 수를 달성하는 데 필요한 최소를 계산하는 데 도움이하자 전에 나는 보았다.
(두 가지 방법이 경우 같은 단지 두 가지의 동일한 세트의 카드를 뒤집어해야하는 경우 간주된다)
나는 그녀에게 보낸 용액 공정 문서화 된 절차, 몇 가지 더 마음에 드는 것이 게임을하는 그녀는 나중에 시간을 넣으면?


입력 형식

각각의 테스트 포인트 테스트 데이터의 여러 세트.
첫 번째 라인은 양의 정수를 가지며, T는 데이터 세트의 수를 나타낸다.
다음에, 각 데이터 세트에 대해, 첫 번째 행은 양의 정수 n,도 10의 테이블에 카드의 개수가있다.
다음에 n 양쪽 모두가 X에 의해 표현되는 두 개의 정수의 X, Y의 라인 카드 (Y), 상방 (X)의 현재 수있다.


출력 형식

각 시험, 반전의 최소 수를 나타내는 라인 출력 두 개의 정수, 프로그램 번호 (998,244,353 모듈의 프로그램 번호)를 참조.
해결책은 "-1 -1."없는 경우


견본

샘플 입력

3
4
1 2
1 2 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 블록 내의 에지들의 개수가 직접 "-1 -1"버튼 출력 유니콤 후 분명히 충족하지 않은 포인트의 수보다 큰 경우가있다.

  경우 2 에 포함 된 링크 블록이 수치 가장자리의 수가 점의 수와 같습니다 후 블록은 우리가 시계 방향으로 실행하고 반 시계 방향으로 각각이 빠른 유니콤, 작은 숫자가 될 방향을 반대로 할 필요를 참조 링 유니콤 트리입니다 양방향 응답 동일한 경우, 프로그램 수는 2, 그렇지 않으면 1이다.

  블록과 동일한 점 -1 유니콤의 가장자리의 숫자가있는 경우 그림 3은 차이나 유니콤은이 블록은 당신이 통계 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 NUM (DP [ST] + PO == DP [ED] + (PO ^ 1).) = 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