zoukankan      html  css  js  c++  java
  • 洛谷P3295 萌萌哒 并查集 + ST表

    又切一道紫题!!!

    成功的(看了一吨题解之后),我A掉了第二道紫题。

    好,我们仔细观察,发现这是一个排列组合问题。

    有些限定条件,要相等的地方,我们就用并查集并起来。最后一查有多少个并查集,就有多少个位置可供自由选择。

    所以答案就是10^(并查集数),去除前导0:*(9/10)

    好,这样我们得到了一个O(mn)算法。

    然后我们考虑优化:每个区间可能被合并多次。所以我们有两种选择:线段树/ST表。

    考虑到这是ST表例题(???????),我们就来个ST表与并查集联动求解...

    我们的ufs[i][j]代表在[i][2^j]这个区间内的情况。

    然后每次合并的时候都往下合并两个j-1(也可以最后再一起下传标记)

    实质上是开了logn个并查集,因为我发现find和merge都不跨层。

    题外话:与RE战斗的艰辛历程

    交了11次RE,实在是让人感受绝望啊。

    两种方法全都RE,所幸刚才我写的时候都查出来错了。

    那么先来看看第一种方法:

    每次都跟线段树一样恰好标记完最少的节点,最后所有标记一起下传。

     1 #include <cstdio>
     2 using namespace std;
     3 const int N = 100010;
     4 const int mo = 1000000007;
     5 
     6 int ufs[N][30],n,m;
     7 int find(int x,int j)
     8 {
     9     if(ufs[x][j]!=x) ufs[x][j]=find(ufs[x][j],j);
    10     return ufs[x][j];
    11 }
    12 void merge(int x,int y,int j)
    13 {
    14     ufs[find(x,j)][j]=find(y,j);///->!!这里调了一个错,之前是ufs[x][j]=......
    15     return;
    16 }
    17 
    18 
    19 int main()
    20 {
    21     scanf("%d%d",&n,&m);
    22     for(int j=0;j<=29;j++)
    23     {
    24         for(int i=1;i<=n;i++) ufs[i][j]=i;///->!
    25     }
    26     int a,b,c,d;
    27     int md=0;
    28     while((1<<md)<=n) md++;
    29     md--;
    30     for(int i=1;i<=m;i++)
    31     {
    32         scanf("%d%d%d%d",&a,&b,&c,&d);
    33         for(int j=md;j>=0;j--)
    34         {
    35             if(a+(1<<j)-1<=b) merge(a,c,j),a+=(1<<j),c+=(1<<j);
    36         }
    37     }
    38     ///
    39     for(int j=md;j>=1;j--)///这里,RE的罪魁祸首!我之前写的0,结果传进去个j=-1直接挂
    40     {
    41         for(int i=1;i+(1<<j)-1<=n;i++)
    42         {
    43             merge(i,find(i,j),j-1);
    44             merge(i+(1<<(j-1)),find(i,j)+(1<<(j-1)),j-1);
    45         }
    46     }
    47     ///
    48     long long ans=9;
    49     bool q=false;
    50     for(int i=1;i<=n;i++)
    51     {
    52         if(find(i,0)==i)
    53         {
    54             if(q) ans=(ans*10)%mo;
    55             q=1;
    56         }
    57     }
    58     //for(int i=1;i<=n;i++) printf("%d ",find(i,0));
    59     printf("%lld",ans);
    60     return 0;
    61 }
    AC代码,跑的比下面快一些

    第二种思路:

    每次都传到底。如果已经在一起就不往下推了。

     1 #include <cstdio>
     2 using namespace std;
     3 const int N = 100010;
     4 const int mo = 1000000007;
     5 
     6 int ufs[N][30],n,m;
     7 int ffind(int x,int j)
     8 {
     9     //if(ufs[x][j]!=x) ufs[x][j]=find(ufs[x][j],j);
    10     //return ufs[x][j];
    11     if(j<0) return 0;
    12     int ans=x,k;
    13     while(ufs[ans][j]!=ans) ans=ufs[ans][j];
    14     while(ufs[x][j]!=x)
    15     {
    16         k=ufs[x][j];
    17         ufs[x][j]=ans;
    18         x=k;
    19     }
    20     return ans;
    21 }
    22 void mmerge(int x,int y,int j)
    23 {
    24     if(j<0) return;///这里控制情况,AC
    25     if(ffind(x,j)==ffind(y,j)) return;
    26     ufs[ffind(x,j)][j]=ffind(y,j);///->!!
    27     mmerge(x,y,j-1),mmerge(x+(1<<(j-1)),y+(1<<(j-1)),j-1);///这里RE!会传入j=-1
    28     return;
    29 }
    30 
    31 
    32 int main()
    33 {
    34     scanf("%d%d",&n,&m);
    35     for(int j=0;j<=29;j++)
    36     {
    37         for(int i=1;i<=n;i++) ufs[i][j]=i;///->!
    38     }
    39     int a,b,c,d;
    40     int md=0;
    41     while((1<<md)<=n) md++;
    42     md--;
    43     for(int i=1;i<=m;i++)
    44     {
    45         scanf("%d%d%d%d",&a,&b,&c,&d);
    46         for(int j=md;j>=0;j--)
    47         {
    48             if(a+(1<<j)-1<=b) mmerge(a,c,j),a+=(1<<j),c+=(1<<j);
    49         }
    50     }
    51     /**
    52  
    53     */
    54     long long ans=9;
    55     bool q=false;
    56     for(int i=1;i<=n;i++)
    57     {
    58         if(ffind(i,0)==i)
    59         {
    60             if(q) ans=(ans*10)%mo;
    61             q=1;
    62         }
    63     }
    64     //for(int i=1;i<=n;i++) printf("%d ",find(i,0));
    65     printf("%lld",ans);
    66     return 0;
    67 }
    AC代码

    结论:函数里最好写上特判违法情况,保险。

  • 相关阅读:
    python 操作ie登陆土豆网
    python网络编程学习笔记(3):socket网络服务器
    python趣味编程:借书方案(排列组合)
    扑克牌洗牌
    C#开发的两个原则的深入讨论(转)
    软件文档知多少?(转)
    反射点滴Common
    使用ASP.NET加密口令(转)
    asp.net 备份和恢复SQL SERVER 数据库
    编程规范程序员们都应该这样写代码(转)
  • 原文地址:https://www.cnblogs.com/huyufeifei/p/8718999.html
Copyright © 2011-2022 走看看