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; }
连通数
很明显的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; }
PolandBall and Gifts
钦定一个人不带礼物会导致两个人收不到礼物,那么一个显而易见的贪心思路就出来了。
最多:
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; }
Scores
分别单独把科目拿来看,看都有具体那些人满足条件。然后合并找出满足这五个条件的个人~~
首先将单科目的人按照从小到达的顺序排序,然后进行二分查找就可以知道一共有多少个人满足条件。
但是我们需要知道有那些人满足条件而不是数量~~。
所以我们维护一个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; }
Metal Processing Plant
不妨设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; }