Preface
bitset,還是一個比較好用的STL,可以給一些題目做到神奇的常數優化(\(O(\frac{原來的復雜度}{機器的位數(32位or64位)})\))
關於一些具體的函數等內容可以參考,這里不再贅述。通過一些簡單的題目看一下實際運用。
Newcoder 132C 簡單瞎搞題
這個東西我們感覺可以用類似背包的方法搞一下,記錄一下哪些數是是當前可以取到的,可以滾存一下。
但是我們考慮到這樣bool數組賦值可能會使復雜度達到\(O(n^4)\),因此我們可以把bool數組改為bitset
這樣更新的時候我們先左移再不停地或累計答案即可。
CODE
#include<cstdio>
#include<cctype>
#include<bitset>
using namespace std;
const int N=1000005;
int n,q,l,r;
bitset <N> ans,t;
inline char tc(void)
{
static char fl[100000],*A=fl,*B=fl;
return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
}
inline void read(int &x)
{
x=0; char ch; while (!isdigit(ch=tc()));
while (x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc()));
}
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
register int i,j; read(n); ans[0]=1;
for (i=1;i<=n;++i)
{
read(l); read(r); t.reset();
for (j=l;j<=r;++j) t|=ans<<j*j; ans=t;
}
return printf("%d",ans.count()),0;
}
POJ 2443
題目大意:給出\(n\)個集合,每個集合中最多有\(10000\)個數,每個數的范圍為\([1,10000]\),給出\(q\)次詢問,每次給出兩個數\(u,v\)判斷是否有一個集合中同時含有\(u,v\)兩個數。
這個十分清晰,我們用bitset記錄每一個數所屬的集合,判斷是否同一集合直接and一下看看有沒有交即可。
CODE
#include<cstdio>
#include<cctype>
#include<bitset>
using namespace std;
const int N=1005,MAX_SIZE=10005;
int n,q,x,y;
bitset <N> bit[MAX_SIZE],t;
inline char tc(void)
{
static char fl[100000],*A=fl,*B=fl;
return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
}
inline void read(int &x)
{
x=0; char ch; while (!isdigit(ch=tc()));
while (x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc()));
}
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
register int i,j; read(n);
for (i=1;i<=n;++i)
for (read(x),j=1;j<=x;++j)
read(y),bit[y].set(i);
for (read(q),i=1;i<=q;++i)
{
read(x),read(y); t=bit[x]&bit[y];
if (t.any()) puts("Yes"); else puts("No");
}
return 0;
}
HDU 5036
題目大意:一個人要打開或者用炸彈砸開所有的門,每個門里面有一些鑰匙,一個鑰匙對應一個門,有了一個門的鑰匙就能打開相應的門,告訴每個門里面有哪些門的鑰匙,問需要用的炸彈為多少。
我們考慮對於每一扇們單獨計算期望,根據期望的線性性質最后累加起來就是答案。
考慮一扇門怎樣才能被打開,當然是用炸彈炸開或者用鎖打開,而用炸彈炸開的話會使用一次炸彈,因此\(E_i=\frac{1}{g_i}\),\(g_i\)表示有多少個點(包括自己)可以到達\(i\)。
考慮這個問題,其實就是一個傳遞閉包,用bitset優化一下floyed即可跑過\(1000\)的數據。
Tarjan+拓撲排序貌似也可以跑,但是根本沒有這個好寫啊
CODE
#include<cstdio>
#include<cctype>
#include<bitset>
using namespace std;
const int N=1005;
bitset <N> d[N];
int t,n,x,y; double ans;
inline char tc(void)
{
static char fl[100000],*A=fl,*B=fl;
return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
}
inline void read(int &x)
{
x=0; char ch; while (!isdigit(ch=tc()));
while (x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc()));
}
inline void floyed(void)
{
for (register int i=1;i<=n;++i)
for (register int j=1;j<=n;++j)
if (d[j].test(i)) d[j]|=d[i];
}
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
register int i,j,s; read(t);
for (s=1;s<=t;++s)
{
for (read(n),i=1;i<=n;++i) d[i].reset();
for (i=1;i<=n;++i)
for (read(x),d[i].set(i),j=1;j<=x;++j)
read(y),d[y].set(i); ans=0;
for (floyed(),i=1;i<=n;++i)
ans+=(double)1/d[i].count();
printf("Case #%d: %.5lf\n",s,ans);
}
return 0;
}