• Bitset 练习


    Bipartite Graph

     (HDU - 5313 

    若干个二分图两侧的点数是定值,现在就是要选择翻转一些二分图,再拼起来,最后使得两边的点数尽量接近,这样可以达到最大。

    考虑背包DP,dp[i]表示i这个值是否凑的出来,有:

    if(dp[i]) dp[i+w[now]]=1

    那么就很套路地使用Bitset优化,每次只要或一下左移后的结果就行了。

    CODE:

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <bitset>
    #include <vector>
    using namespace std;
    const int maxn=1e4+10;
    const int maxm=1e5+10;
    struct point
    {
        int to;
        int nxt;
    }edge[maxm*2];
    struct nod
    {
        int a,b;
        nod(int a,int b):a(a),b(b) {};  
    };
    int n,m,T,na,nb,tot;
    vector<nod> ans;
    int vis[maxn],head[maxn];
    
    inline void add(int u,int v)
    {
        tot++;
        edge[tot].nxt=head[u];
        edge[tot].to=v;
        head[u]=tot;
    }
    
    inline void dfs(int x,int ff)
    {
        if(ff) na++;
        else nb++;
        vis[x]=1;
        for(int i=head[x];i;i=edge[i].nxt)
        {
            int v=edge[i].to;
            if(vis[v]) continue;
            dfs(v,ff^1);
        }
    }
    
    int main()
    {
        scanf("%d",&T);
        while(T--)
        {
            scanf("%d%d",&n,&m);
            tot=0;
            memset(vis,0,sizeof(vis));
            memset(head,0,sizeof(head));
            for(int i=1;i<=m;i++)
            {
                int u,v;
                scanf("%d%d",&u,&v);
                add(u,v),add(v,u);
            }
            ans.clear();
            for(int i=1;i<=n;i++)
            if(!vis[i])
            {
                na=nb=0;
                dfs(i,0);
                ans.push_back(nod(na,nb)); 
            }
            bitset<maxn> aa; 
            aa.set(0);
            int mm=ans.size();
            for(int i=0;i<mm;i++)
                aa=(aa<<ans[i].a)|(aa<<ans[i].b);
            int Max=0;
            for(int i=1;i<=n;i++)
                if(aa[i])
                    Max=max(Max,i*(n-i)-m);
            printf("%d
    ",Max);
        }
        return 0;
    }
    View Code

    连通数

     HYSBZ - 2208 

    很明显的Floyd传递闭包,但是会TLE,开n个Bitset f[i] 表示i可以到达的点集,每次传递的时候只要或一下就行了,注意循环的顺序。

    每次把所有的点都要更新一遍,改成三重循环看一下就知道了。

    CODE:

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <bitset>
    using namespace std;
    const int maxn=2003;
    bitset<maxn> f[maxn];
    int n;
    char ss[maxn];
    
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%s",ss+1);
            for(int j=1;j<=n;j++)
                if(ss[j]-'0' || i==j) f[i][j]=1;
        }
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                if(f[j][i]) f[j]|=f[i];
        int ans=0;
        for(int i=1;i<=n;i++)
            ans+=f[i].count();
        printf("%d",ans);
        return 0;
    }
    View Code

    PolandBall and Gifts

     CodeForces - 755F 

    钦定一个人不带礼物会导致两个人收不到礼物,那么一个显而易见的贪心思路就出来了。

    最多:

    1、对于偶数环,每隔一个就钦定不带,最少需要n/2;

    2、对于奇数环,加一即可。

    记下环的大小,看看能吃掉多少环,最后吃不掉一个环的加上2倍。

    最少:

    连续取破坏性最小,如果恰好有若干的环能凑成k,就是k,否则为k+1。

    这里要用第一题的套路优化DP

    CODE:

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <bitset>
    using namespace std;
    const int maxn=1e6+10;
    int n,k,nn; 
    int a[maxn],vis[maxn];
    int si[maxn],H[maxn],b[maxn];
    
    int main()
    {
        scanf("%d%d",&n,&k);
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        for(int i=1;i<=n;i++)
        if(!vis[i])
        {
            int tmp=i,cnt=0;
            while(!vis[tmp])
            {
                vis[tmp]=1;
                tmp=a[tmp];
                cnt++;
            }
            b[++nn]=cnt;
        }
        sort(b+1,b+nn+1);
        int kk=k,ans1=0,ans2=0;
        for(int i=1;i<=nn;i++)
        {
            if(kk>=b[i]/2) kk-=b[i]/2,ans2+=b[i]/2*2;
            else
            {
                ans2+=kk*2;
                kk=0;
                break;
            }
        }
        ans2+=kk;
        ans2=min(ans2,n);
        for(int i=1;i<=nn;i++) H[b[i]]++;
        bitset<maxn> dp;
        dp.set(0);
        ans1=k+1;
        for(int i=1;i<=n;i++)
        if(H[i])
        {
            int l=1,w=H[i];
            while(w)
            {
                int t=min(w,l);
                w-=t;
                dp|=dp<<(i*t);
                l<<=1;
            }
            if(dp[k])
            {
                ans1=k;
                break;
            }
        }
        printf("%d %d",ans1,ans2);
        return 0;
    }
    View Code

     

    Scores

     HihoCoder - 1236 

    分别单独把科目拿来看,看都有具体那些人满足条件。然后合并找出满足这五个条件的个人~~

    首先将单科目的人按照从小到达的顺序排序,然后进行二分查找就可以知道一共有多少个人满足条件。

    但是我们需要知道有那些人满足条件而不是数量~~。

    所以我们维护一个bitset[i]:=前i个人满足条件时,都有谁

    最后求出的五种情况&一下就可以知道都有多少人剩下了~~

    但是bitset<50001>b[5][50000];   ///b[i][j]:=第i维、前j个人都是谁~~~~~这样会爆内存的。实际上这里是TLE~~~

    所以这里采取分块的方法进行优化~~  

    即bitset<50001>b[5][250];     ///b[i][j]:=第i维、前j个块都有谁~~

    于是当二分找到的位置是idx,位于块k

    利用前k-1块的信息再加上当前块满足条件的几个人就可以得到相应集合了~~

    CODE:

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <bitset>
    #include <cmath>
    using namespace std;
    const int maxn=50001;
    typedef pair<int,int> pii;
    bitset<maxn> bs[5][303],tmp;
    pii val[5][maxn];
    int n,m,q,block;
    
    inline int find(int i,int x)
    {
        int l=0,r=n;
        while(l+1<r)
        {
            int mid=(l+r)>>1;
            if(val[i][mid].first<=x) l=mid;
            else r=mid;        
        }
        return l;
    }
    
    inline void query()
    {
        int x=0,last=0;
        scanf("%d",&q);
        bitset<maxn> ans[5];
        while(q--)
        {
            for(int i=0;i<5;i++) ans[i].reset();
            for(int i=0;i<5;i++)
            {
                scanf("%d",&x);
                x^=last;
                int pos=find(i,x);
                int len=(pos+1)/block;
                int st=len*block;
                if(len>=1) ans[i]=bs[i][len-1];
                else ans[i].reset();
                for(int k=st;k<=pos;k++)
                    ans[i].set(val[i][k].second);
                if(i) ans[i]&=ans[i-1];
            }
            last=ans[4].count();
            printf("%d
    ",last);
        }
    }
    
    int main()
    {
        int T;
        scanf("%d",&T);
        while(T--)
        {
            scanf("%d%d",&n,&m);
            for(int i=0;i<n;i++)
            {
                for(int j=0;j<5;j++)
                {
                    scanf("%d",&val[j][i].first);
                    val[j][i].second=i;
                }
            }
            for(int i=0;i<5;i++) sort(val[i],val[i]+n);
            block=(int)sqrt(n+0.5);
            int idx=0;
            for(int i=0;i<5;i++)
            {
                idx=0;
                for(int j=0;j<n;j+=block,idx++)
                {
                    int nxt=min(j+block,n);
                    bs[i][idx].reset();
                    for(int k=j;k<nxt;k++)
                        bs[i][idx].set(val[i][k].second);
                    if(idx) bs[i][idx]|=bs[i][idx-1];
                }
            }
            query();
        }
        return 0;
    }
    View Code

    Metal Processing Plant

     Gym - 101221G 

    不妨设S1≥S2,从大往小枚举S1,S2的情况就很单调(惯用套路),二分,判断时变成了2-SAT,>S1的不能全在A集合,>S2的不能全在B集合。

    进行优化,发现>S1的两点无论如何不能在同一个集合,并查集判断矛盾,出现矛盾就不用再枚举。

    CODE:

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <stack>
    using namespace std;
    const int maxn=603;
    const int maxm=1e6+10;
    struct point
    {
        int x,y;
        long long w;
        int to;
        int nxt;
    }edge[maxm],e[maxm];
    int ans,dis[203][203];
    int n,tot,cnt,ncon,idx;
    int head[maxn],low[maxn],dfn[maxn],be[maxn];
    int d[maxn],fath[maxn],vis[maxn];
    stack<int> st;
    
    inline int read() 
    {  
        int f = 1, x = 0; char ch = getchar();  
        for(; ch < '0' || ch > '9'; ch = getchar()) f = ch == '-' ? -1 : 1;  
        for(; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0';  
        return f * x;  
    } 
    
    inline void add(int u,int v)
    {
        tot++;
        edge[tot].nxt=head[u];
        edge[tot].to=v;
        head[u]=tot;
    }
    
    inline bool cmp(const point &aa,const point &bb)
    {
        return aa.w>bb.w;
    }
    
    inline int father(int x)
    {
        if(x==fath[x]) return x;
        int f=father(fath[x]);
        d[x]^=d[fath[x]];
        return fath[x]=f;
    }
    
    inline void Tarjan(int x)
    {
        vis[x]=1;
        low[x]=dfn[x]=++idx;
        st.push(x);
        for(int i=head[x];i;i=edge[i].nxt)
        {
            int v=edge[i].to;
            if(!dfn[v])
            {
                Tarjan(v);
                low[x]=min(low[x],low[v]);
            }
            else if(vis[v])
                low[x]=min(low[x],dfn[v]);
        }
        if(low[x]==dfn[x])
        {
            ncon++;
            int now=0;
            while(now!=x)
            {
                now=st.top();
                st.pop();
                vis[now]=0;
                be[now]=ncon;
            }
        }
    }
    
    inline bool check(int s1,int s2)
    {
        tot=0;idx=0;ncon=0;
        for(int i=0;i<=n*2;i++) head[i]=vis[i]=be[i]=dfn[i]=low[i]=0;
        while(!st.empty()) st.pop();
        for(int i=1;i<=n;i++)
            for(int j=i+1;j<=n;j++)
            {
                if(dis[i][j]>s1)
                {
                    add(i<<1,(j<<1)-1);
                    add(j<<1,(i<<1)-1);
                }
                if(dis[i][j]>s2)
                {
                    add((i<<1)-1,j<<1);
                    add((j<<1)-1,i<<1);
                }
            }
        for(int i=n*2;i>=1;i--) if(!dfn[i]) Tarjan(i);
        for(int i=1;i<=n;i++) if(be[i<<1]==be[(i<<1)-1]) return false;
        return true;
    }
    //
    //inline int find(int x)
    //{
    //    int l=-1,r=x;
    //    while(l+1<r)
    //    {
    //        int mid=(l+r)>>1;
    //        if(check(x,mid)) r=mid;
    //        else l=mid;
    //    }
    //    return r;
    //}
    
    inline int find(int c) 
    {  
        int l = 0, r = c;  
        while(l <= r) 
        {  
            int mid = l + r >> 1;  
            if(check(c, mid)) r = mid - 1;  
            else l = mid + 1;  
        }  
        return l;  
    }  
    
    int main()
    {
        while(scanf("%d",&n)!=EOF)
        {
            ans=0x3f3f3f3f;
            for(int i=1;i<=n;i++) fath[i]=i,d[i]=0;
            for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) dis[i][j]=0;
            cnt=0;
            for(int i=1;i<n;i++)
                for(int j=i+1;j<=n;j++) 
                {
                    dis[i][j]=read();
                    dis[j][i]=dis[i][j];
                    e[++cnt].x=i,e[cnt].y=j,e[cnt].w=dis[i][j];    
                }
            if(n<=2) 
            {  
                printf("0
    ");  
                return 0;  
            } 
            sort(e+1,e+cnt+1,cmp);
            for(int i=1;i<=cnt;i++)
            {
                int u=e[i].x,v=e[i].y,ww=e[i].w;
                int f1=father(u),f2=father(v);
                if(f1!=f2)
                {
                    ans=min(ans,(ww+find(ww)));
                    fath[f1]=f2;
                    d[f1]=d[u]^d[v]^1;
                }
                else if(d[u]==d[v])
                {
                    ans=min(ans,(ww+find(ww)));
                    break;
                }
            }
            printf("%d
    ",ans);        
        }
        return 0;
    }
    View Code
  • 相关阅读:
    JS监听组合按键
    XSS初体验
    debuggap,移动端调试新方式
    简析分页逻辑
    【译】Javascript中的数据类型
    【译】typeof null的前世今生
    一个跨域请求的XSS漏洞再续
    H5页面音频自动播放问题
    一个跨域请求的XSS续
    成功自我管理之压力管理
  • 原文地址:https://www.cnblogs.com/linda-fcj/p/9115978.html
Copyright © 2020-2023  润新知