好博客: https://www.cnblogs.com/cjjsb/p/9751384.html
例题1:Newcoder 132C 简单瞎搞题
题目链接:https://www.nowcoder.com/acm/contest/132/C
题意:
分析:核心就在于看作01背包的形式,枚举种类n,每个范围(L-R+1),以及所有的可能值 1~1e6,
然后直接做的话又会超时,所以要用到 bitset 的位运算,将b[0][0]设置为1,每次向左移动 j*j 位,最后就可以得到经过所有组合后的可能取值。
#include#include using namespace std; bitset<1000005>b[105]; int n,l[105],r[105]; int main() { scanf("%d",&n); int Min=0; for(int i=1;i<=n;i++) {scanf("%d%d",&l[i],&r[i]); Min+=l[i]*l[i];} b[0].set(0); //上面那句就等同于 -> b[0][0]=1; for(int i=1;i<=n;i++) for(int j=l[i];j<=r[i];j++) b[i]|=(b[i-1]<<(j*j)); // int ans=0; // for(int i=Min; i<=1000005; i++){ // if(b[n][i]) ans++; // } // printf("%d\n", ans); printf("%d\n",b[n].count()); return 0; }
例题2:POJ-2443
题目链接:https://vjudge.net/problem/POJ-2443
题意:分析某两个元素是否存在于共同的集合。
#include#include #include using namespace std; const int maxn = 1e5+3; bitset<1000> a[10000],t; int main(){ int q,n,m,k; scanf("%d",&n); for(int i=0; i ){ scanf("%d",&m); for(int j=0; j ){ scanf("%d",&k); a[k][i] = 1; } } scanf("%d",&q); int x,y; for(int i=0; i ){ scanf("%d%d",&x,&y); t = a[x]&a[y]; if( t.count() ){ printf("Yes\n"); } else{ printf("No\n"); } } }
例题3:HDU 5036
题意:一个人要打开或者用炸弹砸开所有的门,每个门里面有一些钥匙,一个钥匙对应一个门,有了一个门的钥匙就能打开相应的门,告诉每个门里面有哪些门的钥匙,问需要用的炸弹为多少。
我们考虑对于每一扇们单独计算期望,根据期望的线性性质最后累加起来就是答案。
分析:利用 bitset 优化 floyed 传递闭包 (利用 bitset 优化计算个数的过程)
就是不需要用到三重循环,而是把一个节点的父亲节点的所有父亲节点累计在这个孙子 bitset 上。
(即如果一个点是另一个点的父亲,那么父亲的父亲也都能够到达这个点,那就可以全部记录在这个孙子 bitset 上了 )
#include#include #include using namespace std; const int maxn = 1e3+3; bitset a[maxn]; int main(){ int T,kase=1; scanf("%d",&T); while(T--){ int n,x,y; scanf("%d",&n); for(int i=1; i<=n; i++) a[i].reset(); for(int i=1; i<=n; i++){ a[i].set(i); scanf("%d",&x); for(int j=1; j<=x; j++){ scanf("%d",&y); a[y].set(i); } } for(int i=1; i<=n; i++){ for(int j=1; j<=n; j++){ if(a[j].test(i)){ a[j] |= a[i]; } } } double ans = 0; for(int i=1; i<=n; i++){ ans += 1.0/(a[i].count()); // printf("%d %lf\n", i,ans); } printf("Case #%d: %.5lf\n",kase++, ans); } }