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\n",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\n",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\n");  
            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\n",ans);        
    }
    return 0;
}
View Code

 

posted @ 2018-05-31 12:04  Captain_fcj  阅读(300)  评论(0编辑  收藏  举报