哪一天她能重回我身边

Solution

将每个卡片反面数字向正面数字(值域\(1~2n\))建边,若能通过翻转边的方向使每个点的入度不大于\(1\),则达到目标
对于’使每个点的入度不大于\(1\),我们让其构成一棵树,(根节点入度为\(0\)
最终答案:最小次数=\(\sum各联通块最小次数\),方案数=\(\prod各联通块最小次数的个数\)
对于每个联通块,边数\(m\),点数\(n\)

\(m==n-1\)
\(dfs\)出联通块中任意一点\(x\)\(f[x]\),即以\(x\)为根,总翻转次数。
\(dfs\)一遍得出每个点的\(f[]\),取个\(min\),并找一下有几个\(min\)

\(m==n\)
基环树,只有两种情况,选一条环上的边\((ed_{id})\),符合要求的图的根节点只能是这条边两端端点\((S和T)\)之一
先忽略这条边,最后把这条边指向根节点,算出\(f[S],f[T]\).取\(min\)及找\(min\)个数\((1或2)\)

\(m>n\) 一定无法达到

处理的时候建成双向边,\(cnt\)初始为1,用来判断父边和判断正面向反面的边。

code

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
#include <cctype>
using namespace std;
char buf[1<<20],*p1,*p2;
#define rint register int
#define gc() (p1==p2?(p2=buf+fread(p1=buf,1,1<<20,stdin),p1==p2?EOF:*p1++):*p1++)
#define read() ({\
	rint x=0;register char ch=gc();\
	while(!isdigit(ch)) ch=gc();\
	while(isdigit(ch)) x=(x<<3)+(x<<1)+(ch&15),ch=gc();\
	x;\
})
const int maxn=2e5+5,M=998244353;
int f[maxn];
int S,T,ed_id;
int ans,ans_cnt;
int n,cnt,head[maxn];
int node_cnt,edge_cnt;
bool vis[maxn],jud;
struct Edge{
	int to,next;
}e[maxn*2];
vector < int > vec;
void Init(){
	cnt=1,jud=0,ans=0,ans_cnt=1;
	memset(head,0,sizeof head);
	memset(vis,0,sizeof vis);
	memset(f,0,sizeof f);
}
void Aedge(rint x,rint y){
	e[++cnt].to=y;
	e[cnt].next=head[x];
	head[x]=cnt;
}
void dfs(rint x){
	vis[x]=1,node_cnt++;
	for(rint i=head[x];i;i=e[i].next){
		const rint y=e[i].to;
		edge_cnt++;
		if(vis[y]) continue;
		dfs(y);
	}
}
void dfs1(rint x,rint prt){
	vis[x]=1;
	for(rint i=head[x];i;i=e[i].next){
		const rint y=e[i].to;
		if(y==prt) continue;
		if(vis[y]) S=x,T=y,ed_id=i;
		else{
			dfs1(y,x);
			f[x]+=f[y]+(i&1);
		}
	}
}
void dfs2(rint x,rint prt){
	vec.push_back(f[x]);
	for(rint i=head[x];i;i=e[i].next){
		const rint y=e[i].to;
		if(y==prt||i==ed_id||i==(ed_id^1)) continue;
		f[y]=f[x]+((i&1)?-1:1);
		dfs2(y,x);
	}
}
int main(){ 
	//freopen("back10.in","r",stdin);
	//freopen("1.out","w",stdout);
	freopen("back.in","r",stdin);
	freopen("back.out","w",stdout);
	rint cs=read();
	while(cs--){
		Init();
		n=read();
		for(rint i=1,x,y;i<=n;++i){
			x=read(),y=read();
			Aedge(y,x),Aedge(x,y);
		}
		for(rint i=1;i<=n*2;++i){
			if(vis[i]) continue;
			node_cnt=0,edge_cnt=0;
			dfs(i);
			if(edge_cnt/2>node_cnt){ jud=1;break; }
		}
		if(jud){ printf("-1 -1\n");continue; }
		memset(vis,0,sizeof vis);
		for(rint i=1;i<=n*2;++i){ // 枚举每个点作为根节点指向儿子,以保证每点入度不大于1
			if(vis[i]) continue;
			vec.clear();
			S=0,T=0,ed_id=0;
			dfs1(i,0);
			dfs2(i,0);
			rint cc=0;
			if(!ed_id){
				sort(vec.begin(),vec.end());
				for(rint j=0;j<vec.size();++j){
					if(vec[j]==vec[0]) ++cc;
					else break;
				}
				(ans+=vec[0])%=M;
				ans_cnt=1ll*ans_cnt*cc%M;
			}
			else{
				const rint opt=ed_id&1;
				if(f[S]+(!opt)==f[T]+opt) cc=2; // 注意不要'if(~~) f[S]++; else f[T]++; ',可能S==T
				else cc=1;
				(ans+=min(f[S]+(!opt),f[T]+opt))%=M;
				ans_cnt=1ll*ans_cnt*cc%M;
			}
		}
		printf("%d %d\n",ans,ans_cnt);
	}
	return 0;
}
posted @ 2020-10-12 10:05  liuzhaoxu  阅读(135)  评论(0编辑  收藏  举报