这是最终章,永久的思念。
题目大意:
有N张牌,每张牌两面都有数字,范围都在1到2N之间,求最少的反转次数,使得每张牌朝上的一面的数字各不相同,并求出达到这个效果的方案数。(多测,初始时每张牌正面朝上,无解输出“-1 -1”)
题解:
20%数据(N<=20)
直接搜索即可。
100%数据(N<=1×105)
搜索复杂度不允许,我们试图让这个问题抽象化。
将牌上的数字抽象为点,牌抽象为边,则问题转化为:
对于一张有向图,将某些边翻转,使得每个点的入度小于等于1。
先连边,我们发现整张图最多有2N个点,却只有N条边。这代表图的连通性极差,图被分成许多联通块,然后我们对于每个联通块进行分类讨论。
设连通块的边数为e,点数为v。
若e<v,则整张图一定无解,根据抽屉原理,至少有一个点的入度大于1;
若e=v,则该图是一颗基环树,环上的点入度不能为0,因为环上的点一定会被另一个环上的点指向,要使条件成立,则一定是一颗基还外向树;
若e=v+1,则该图是一棵树,可以先后进行两次dfs,求出所需要的最小花费。
第一次任选一点进行dfs,由儿子向父亲更新,若通过一条反边,则在f数组上加1,若dfs的是一颗基环树,则还需要记录未经过的一条边,防止第二次dfs的路径与第一次不同。然后进行第二次,由父亲向儿子更新,若通过反边,则在g数组上加1,反之减1。g数组与f数组在搜索树的根处相等。
在进行第二次dfs时,将每个节点的g值压进一个vector里面,然后sort一下,第一个便是最小值,与最小值相同的数的个数便是方案数。
最后相乘即可。
单次复杂度O(NlogN)
Code:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include<iostream>
2 #include<cstdio>
3 #include<cstring>
4 #include<vector>
5 #include<algorithm>
6 #define LL long long
7 using namespace std;
8 const int N=100010;
9 const LL mod=998244353;
10 int t,n,m=1,mv=0,me=0,cnt=0,tot=0,top=0,start,endd,bridge;
11 int fi[N<<1],g[N<<1],f[N<<1];
12 bool v[N<<1],vis[N<<1];
13 struct edge{
14 int u,v,ne;
15 }e[N<<1];
16 vector<int> gg;
17 void add(int x,int y)
18 {
19 e[++m].u=x;
20 e[m].v=y;
21 e[m].ne=fi[x];
22 fi[x]=m;
23 }
24 int read()
25 {
26 int s=0;
27 char c=getchar();
28 while(c<'0'||c>'9') c=getchar();
29 while(c>='0'&&c<='9'){
30 s=(s<<3)+(s<<1)+c-'0';
31 c=getchar();
32 }
33 return s;
34 }
35 void clean()
36 {
37 memset(fi,0,sizeof(fi));
38 memset(e,0,sizeof(e));
39 memset(f,0,sizeof(f));
40 memset(g,0,sizeof(g));
41 memset(v,false,sizeof(v));
42 memset(vis,false,sizeof(vis));
43 m=1;top=tot=cnt=0;
44 }
45 void dfs(int x)
46 {
47 v[x]=true;mv++;
48 for(int i=fi[x];i!=0;i=e[i].ne){
49 int y=e[i].v;
50 me++;
51 if(v[y]) continue;
52 dfs(y);
53 }
54 }
55 void dfs1(int x,int p)
56 {
57 vis[x]=true;
58 for(int i=fi[x];i!=0;i=e[i].ne){
59 int y=e[i].v;
60 if(y==p) continue;
61 if(vis[y]){
62 start=x;endd=y;bridge=i;
63 }
64 else{
65 dfs1(y,x);
66 f[x]+=f[y]+(i&1);
67 }
68 }
69 }
70 void dfs2(int x,int p)
71 {
72 gg.push_back(g[x]);
73 for(int i=fi[x];i!=0;i=e[i].ne){
74 int y=e[i].v;
75 if(y==p||i==bridge||i==(bridge^1)) continue;
76 g[y]=g[x]+((i&1)==1?-1:1);
77 dfs2(y,x);
78 }
79 }
80 int main()
81 {
82 t=read();
83 while(t--)
84 {
85 clean();
86 n=read();
87 for(int i=1;i<=n;i++){
88 int x=read(),y=read();
89 add(y,x);add(x,y);
90 }
91 int flag=0;
92 for(int i=1;i<=2*n;i++){
93 if(!v[i]){
94 mv=me=0;
95 dfs(i);
96 if(me/2>mv){
97 printf("-1 -1\n");
98 flag=1;break;
99 }
100 }
101 }
102 if(flag==1) continue;
103 int ans1=0;LL ans2=1;
104 for(int i=1;i<=2*n;i++){
105 if(!vis[i]){
106 LL num=0;start=endd=bridge=0;
107 gg.clear();
108 dfs1(i,0);
109 g[i]=f[i];
110 dfs2(i,0);
111 if(bridge==0){
112 sort(gg.begin(),gg.end());
113 ans1+=gg[0];
114 for(int j=0;j<gg.size();j++){
115 if(gg[j]!=gg[0]) break;
116 num++;
117 }
118 }
119 else{
120 bridge&=1;
121 if(g[start]+(bridge^1)==g[endd]+bridge) num=2;
122 else num=1;
123 ans1+=min(g[start]+(bridge^1),g[endd]+bridge);
124 }
125 ans2=ans2*num%mod;
126 }
127 }
128 printf("%d %lld\n",ans1,ans2);
129 }
130 return 0;
131 }