码迷,mamicode.com
首页 > 其他好文 > 详细

单身三连之三

时间:2019-07-22 20:02:50      阅读:127      评论:0      收藏:0      [点我收藏+]

标签:mes   次数   get   getchar   一点   etc   als   ns2   tar   

这是最终章,永久的思念。

题目大意:

  有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:

技术图片
  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 }
View Code

 

 

单身三连之三

标签:mes   次数   get   getchar   一点   etc   als   ns2   tar   

原文地址:https://www.cnblogs.com/hz-Rockstar/p/11227914.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!