5765 Bonds 高維字首和
阿新 • • 發佈:2019-02-07
這題做了好久,,看了網上的題解也看不太懂,,只知道用高維字首做,,,,,,然後在拉粑粑的時候,,突然想出了一種方法
他是要算出每條邊出現在多少割集中,,,我們可以反著想,,我們可以先算出有多少割集(個數設為sum),在算出這條邊包含多少合法聯通塊中(個數設為x),,然後sum-x就是該條邊在多少割集中,,
那怎麼算有多少個聯通快中有這條邊那。。。
假設這條的兩端是u和v,,,那轉化為二進位制就是第u位和第v位1,,,,也就是說,,一個用二進位制表示的聯通快的第u位和第v位為1的話,,就說明該聯通快包含這條邊,,然後我們就是求,包含u,v這兩個二進位制位的集合有多少個,,,這不就是高維字首和所求的嗎。
#include<algorithm> #include<iostream> #include<cstring> #include<queue> #include<cmath> #include<cstdio> using namespace std; #define mem(x,y) memset(x,y,sizeof(x)) #define FIN freopen("input.txt","r",stdin) #define fuck(x) cout<<x<<endl const int MX=21; #define INF 0x3f3f3f3f #define lson l,m,rt<<1 typedef long long LL; #define rson m+1,r,rt<<1|1 int n,m; int G[MX]; int dp[1<<MX],pre[1<<MX]; int w[MX*MX][2]; int main() { FIN; int T; cin>>T; int cas=0; while(T--) { printf("Case #%d: ",++cas); mem(G,0); scanf("%d%d",&n,&m); for(int i=1; i<=m; i++) { int u,v; scanf("%d%d",&u,&v); G[u]|=(1<<v); G[v]|=(1<<u); w[i][0]=u; w[i][1]=v; } int sum=0;//總共有幾個合法的割 dp[0]=dp[(1<<n)-1]=0; pre[0]=pre[(1<<n)-1]=0; for(int i=1; i<(1<<n)-1; i++)//判斷子集的聯通性 { int flag=0; if((-i&i)^i)//可以使用樹狀陣列的lowbit,來快速求出二進位制位是否只有一個1 { for(int j=0; j<n; j++)if((i&(1<<j))&&dp[i^(1<<j)]&&(i&G[j])) { flag=1; break; } } else flag=1; dp[i]=flag; } for(int i=1; i<(1<<n)-1; i++)//有幾個合法的割 { pre[i]=dp[i]&dp[(~i)&((1<<n)-1)]; sum+=pre[i]; } for(int i=0; i<n; i++)//高維字首和 for(int j=(1<<n)-1; j>=1; j--) if(~j&(1<<i)) pre[j]+=pre[j^(1<<i)]; for(int i=1; i<=m; i++)printf("%d%c",sum/2-pre[(1<<w[i][0])|(1<<w[i][1])],i==m?'\n':' '); } return 0; }