• 单身三连之三


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

    题目大意:

      有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
    ");
     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
    ",ans1,ans2);
    129     }
    130     return 0;
    131 }
    View Code

     

     

  • 相关阅读:
    5.4Java Collections工具类 != Collection接口没关系
    4.30Java 手动敲简易的HashSet
    5.4Java使用容器存储表格数据
    4.30Java Iterator迭代器遍历容器元素(List/Set/Map)
    5.4Java IO流开篇
    windows管理规范WMI
    META标签的奥妙
    C#2.0泛型--Dictionary,List用法
    Win32类及其管理对象
    Asp.net中GridView使用详解(引)
  • 原文地址:https://www.cnblogs.com/hz-Rockstar/p/11227914.html
Copyright © 2020-2023  润新知