• 【Bitset】重识


    ----------------------------------------------------------------------------

    一题题目:

    一题题解:

     

    • 这个题目哪来入门再好不过了,支老板之前没有接触过这个东西,然后一点即通:就是把一个int(32位)拆成32个只放0或1的位置,然后这32个的单点操作或者32个一起操作的复杂度是O(1),所以长度位N的bitset的一次单点操作是O(1),整体操作是O(N/w),其中w=32。(long long 是64)。
    • 然后Bitset还有强大是&和|功能,以及count(1)等功能。
    • 但是Bitset最厉害的地方在于优化图论的dfs部分: 我们知道,dfs的时候对于当前点X,会继续访问和X有关联的所有点,continue忽略vis过的点。而有了bitset,我们可以直接得到有关联的,而且没有vis过的点,这样可以保证不去访问不必要的边,这在稠密图里改进极大。

     

    -----------------------------------------------------------------------------

    二题题目:

    二题题解:

    • 跳过缩点建图。
    • 第一部分是求Bitset,得到每个点可以访问到的点。 
    • 第二部分求对应的权值和,巧妙的利用了分块来优化,复杂度/13。而且这个13的选择也是经过计算的,2^13<n,保证分块计算的复杂度影响小于前面的复杂度,新技能,学到了。

    POJ3275:Ranking the Cows

    题意:有N头奶牛,现在给出M对产奶量关系U>V,问至少还需要知道多少奶牛可以做到全部奶牛产奶关系。

    思路:有向图,问至少再加多少边,使得任意两点S、T的可以到达(S到达T或者到达S)。闭包传递后不能到达的需要加边,ans++。

     至于为什么ans++,可以参考,http://www.cnblogs.com/hua-dong/p/8538980.html

    Floyd稍微优化:    注意边少,可以加一维限制,勉强通过。

    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    const int maxn=1010;
    int mp[maxn][maxn];
    int main()
    {
        int N,M,ans,i,j,k;
        while(~scanf("%d%d",&N,&M)){
            ans=0;
            memset(mp,0,sizeof(mp));
            for(i=1;i<=M;i++){
                scanf("%d%d",&j,&k);
                mp[j][k]=1;
            }
            for(k=1;k<=N;k++)
             for(i=1;i<=N;i++)
              if(mp[i][k])
               for(j=1;j<=N;j++)
                if(mp[k][j]) mp[i][j]=1;
            for(i=1;i<=N;i++)
             for(j=i+1;j<=N;j++)
              if(!mp[i][j]&&!mp[j][i])
               ans++;
            printf("%d
    ",ans);
        }
        return 0;
    }
    View Code

    Bitset优化Floyd: 复杂度降为O(N^3/32)<3*10^7。 

    #include<cstdio>
    #include<bitset>
    #include<cstdlib>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    const int maxn=1010;
    bitset<maxn>mp[maxn];
    int main()
    {
        int N,M,ans,i,j,k;
        while(~scanf("%d%d",&N,&M)){
            ans=0;memset(mp,0,sizeof(mp));
            for(i=1;i<=M;i++){
                scanf("%d%d",&j,&k);
                mp[j].set(k);
            }
            
            for(k=1;k<=N;k++)
             for(i=1;i<=N;i++)
              if(mp[i][k])
               mp[i]|=mp[k];
            for(i=1;i<=N;i++)
             for(j=i+1;j<=N;j++)
              if(!mp[i][j]&&!mp[j][i])
               ans++;
            printf("%d
    ",ans);
        }
        return 0;
    }
     
    View Code

     

    POJ2443:Set Operation

    题意:给定N个集合,Q次询问,对每次询问,求X,Y是否在同一集合出现过,注意X=Y时,X在一个集合里至少出现一次就满足了。

    思路:用Bitset来表示每个元素在哪些集合出现过,如果X和Y出现的集合有交集,则满足。

    #include<cstdio>
    #include<bitset>
    #include<cstdlib>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    const int maxn=10000;
    bitset<1010>s[maxn+10];
    int read()
    {
        char c=getchar(); int res;
        while(c>'9'||c<'0') c=getchar();
        for(res=0;c>='0'&&c<='9';c=getchar()) res=(res<<3)+(res<<1)+c-'0';
        return res;
    }
    int main()
    {
        int N,Q,i,num,x,y;
        while(~scanf("%d",&N)){
            for(i=1;i<=maxn;i++) s[i].reset();
            for(i=1;i<=N;i++){
                num=read();
                while(num--){
                    x=read();
                    s[x][i]=1; //s[x].set(i);
                }
            }
            Q=read();
            while(Q--){
                x=read(); y=read();
                if((s[x]&s[y]).count()) puts("Yes"); //.any()
                else puts("No");
            }
        }
        return 0;
    }
    View Code

    AtCoder - 3857:Median Sum

    题意:给定N个数,有2^N-1种非空组合,求这些组合的和排序后的中位数。

    思路:由对称性,知道中位数略大于Sum/2,所以我们从(Sum+1)/2后面所有的可能中,选择最近的一个。(证明见下面。)

    具体实现:需要得到背包结果有哪些可能性,这个只需要Bitset加速一下即可得到。

    证明:见前辈写的,很清晰,只要利用对称性即可。http://blog.csdn.net/zzzzone/article/details/79115522

    #include<bitset>
    #include<cstdio>
    #include<cstdlib>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    const int maxn=2002;
    bitset<maxn*maxn+10>s;
    int main()
    {
        int N,x,ans;
        while(~scanf("%d",&N)){
            s.reset(); ans=0;
            s[0]=1;
            for(int i=1;i<=N;i++){
                scanf("%d",&x);
                s|=(s<<x);
                ans+=x;
            }
            for(int i=(ans+1)/2;;i++){
                if(s[i]){
                   printf("%d
    ",i);
                   break;
                }
            }
        }
        return 0;
    }
    View Code

    CodeForce 914F:Substrings in a String

    题意:

               给定一个小写字符组成的字符串S;现在有Q种操作,对于每个操作Q,输入opt,如果opt==1,输入x,c,表示把S[x]改为c,(c是小写字母)。 如果opt==2,输入字符串T,输出S种有多少个字串==T(字串可以重叠),

    思路:

              1,假设我们要统计S里有多少个T,先统计S里面字符==T[0]的是哪些,然后统计S中有T[0]的位置后面跟的字符==T[1]的有哪些,然后统计S中有T[0]的位置后面跟的字符==T[1]的而且后面跟的字符==T[2]的有哪些.....直到对比到S[len-1]。    

              2,最后利用位移可以得到某个区间的1的个数。

    #include<bitset>
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    const int maxn=100010;
    bitset<maxn>s[27],ans;
    char a[maxn],b[maxn],c[3];
    void read(int &res)
    {
        char c=getchar();
        while(c>'9'||c<'0') c=getchar();
        for(res=0;c>='0'&&c<='9';c=getchar()) res=(res<<3)+(res<<1)+c-'0';
    }
    int main()
    {
        scanf("%s",a+1);
        int T=strlen(a+1);
        int i,j,l,r,Q,opt;
        for(i=1;i<=T;i++)
            s[a[i]-'a'].set(i);
        read(Q);
        while(Q--){
            read(opt);
            if(opt==1){
                scanf("%d%s",&j,c);
                s[a[j]-'a'][j]=0;
                s[(a[j]=c[0])-'a'][j]=1;
            }
            else {
                read(l); read(r);
                scanf("%s",b);
                int S=strlen(b);
                if(S>r-l+1) {
                     puts("0"); continue;
                }
                ans.set();
                for(i=0;i<S;i++){
                    ans&=(s[b[i]-'a']>>i);
                }
                printf("%d
    ",(ans>>l).count()-(ans>>(r-S+2)).count());
            }
        }
        return 0;
    }
    View Code

    CodeForces-707D:Persistent Bookcase

    题意:现在有一个N*M的书架,有Q个操作,对于每个操作,输入opt:

              如果opt==1,那么输入x,y,如果第x行第y列无书,则放一本书。

              如果opt==2,那么输入x,y,如果第x行第y列有书,则取走那本书。

              如果opt==3,那么输入x,将第x行有书的取走,无书的位置放一本。

              如果opt==4,那么输入k,表示把书架的情况恢复为第k次操作后的样貌,k在当前操作之前。

    思路:初看可能是可持久化数据结构,但是注意到整体操作顺序为有根树,可以DFS回溯,对于书架上的情况,可以直接积累或者Bitset假设。

    #include<bitset>
    #include<cstdio>
    #include<cstdlib>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    const int maxn=1010;
    const int maxm=100010;
    bitset<maxn>s[maxn],P;
    int N,M,Q;
    int Laxt[maxm],Next[maxm],To[maxm],cnt;
    int opt[maxm],x[maxm],y[maxm],ans[maxm];
    void read(int &res)
    {
        char c=getchar(); res=0;
        for(;c>'9'||c<'0';c=getchar());
        for(;c<='9'&&c>='0';c=getchar()) res=(res<<3)+(res<<1)+c-'0';
    }
    void add(int u,int v)
    {
        Next[++cnt]=Laxt[u];
        Laxt[u]=cnt;
        To[cnt]=v;
    }
    void dfs(int u,int Now)
    {
        for(int i=Laxt[u];i;i=Next[i]){
            int v=To[i];
            if(opt[v]==1&&s[x[v]][y[v]]==0) {
                    s[x[v]][y[v]]=1;
                    ans[v]=Now+1;
                    dfs(v,ans[v]);
                    s[x[v]][y[v]]=0;
            }
            else if(opt[v]==2&&s[x[v]][y[v]]==1) {
                    s[x[v]][y[v]]=0;
                    ans[v]=Now-1;
                    dfs(v,ans[v]);
                    s[x[v]][y[v]]=1;
            }
            else if(opt[v]==3){
                    ans[v]=Now-s[x[v]].count();  
                    s[x[v]]^=P;  
                    ans[v]+=s[x[v]].count(); 
                    dfs(v,ans[v]);
                    s[x[v]]^=P;
            }
            else {
                    ans[v]=Now;
                    dfs(v,ans[v]);
            }
         }
    }
    int main()
    {
        read(N); read(M); read(Q);
        for(int i=1;i<=M;i++) P.set(i);
        for(int i=1;i<=Q;i++){
            scanf("%d",&opt[i]);
            if(opt[i]==1||opt[i]==2) read(x[i]),read(y[i]);
            else read(x[i]);
            if(opt[i]==4) add(x[i],i);
            else add(i-1,i);
        }
        dfs(0,0);
        for(int i=1;i<=Q;i++) printf("%d
    ",ans[i]);
        return 0;
    }
    View Code

    还有两个很重要的函数,去遍历哪些为1的地方,bitset._Find_first(),和bitset._Find_next(i)

  • 相关阅读:
    php设计模式-装饰器模式
    php设计模式-观察者模式
    play-with-docker搭配ffsend完成文件上传及下载(解决从docker hub拉取镜像慢问题)
    java执行命令行,以及解决卡死问题
    在docker容器中调用docker命令
    MIME 类型,ContentType
    大文件下载---之内存溢出问题解决
    OperationCenter Docker运行环境及其依赖启动脚本
    快速获取批量处理Docker镜像SQL语句
    解决达梦数据库新增大字段报错问题
  • 原文地址:https://www.cnblogs.com/hua-dong/p/8519739.html
Copyright © 2020-2023  润新知