Description
- 對於一個兩邊各有n個點的二分圖的左邊一個點集A,定義F(A)爲右邊至少和A中的一個點相鄰的點的集合。
- 給定一個K,要求構造一個二分圖,使得對於所有點集A,∣F(A)∣<∣A∣有恰好K次成立。
Data Constraint
- n≤32,K∈[0,2n]。
Solution
- 對於左邊的點,假定第i個連向的點完全包含第i−1個連向的點,且di−di−1≤1(di表示左邊點i的度數),則我們有一個結論:
- 對於一種方案,將其d1∼dn壓縮成一個二進制數(dndn−1dn−2……d1)2;然後將這些數按1的個數爲第一關鍵字從少到多、按大小爲第二關鍵字從大到小排序,則最後某個數的序號即爲該方案的∣F(A)∣≥∣A∣成立次數(記爲id)。
- 考慮證明上述結論。
- 對於一種方案,記Bx爲某個滿足di−1=di−1的i(包括1),則有B(1,2,……,m)(m爲不同的度數個數)。若不考慮空集,則:
cnt=i=1∑nj=1∑di(i−1j−1)=i=1∑m(ni)−(Bi−1i)
- 那麼比如有一種方案(0100)2,我們將其中的1右移一位變成(0010)2,其實相當於令d2++,則根據左式id++。也就是說,對於一種方案的二進制數,若其中的1不全在最右,則我們必能右移某一個1使其id++。
- 而如果某一狀態的二進制數中的1全在最右,比如(00000111)2,則其id++就會變成(11110000)2。因爲根據右式,(00000111)2的cnt=(81)+(82)+(83),(11110000)2的cnt與其的差值爲cnt=(84)−(74)−(63)−(52)−(41)=(40)=1。其他以此類推。
- 於是,我們可以先預處理出組合數,減一減算出要求的方案的二進制數中1的數量,以及它在那些二進制數的第幾位;繼而算出該方案的每一位。
Code
#include <bits/stdc++.h>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
ll i,j,n,k,C[33][33],x,a[33];
int main()
{
freopen("circle.in","r",stdin);
freopen("circle.out","w",stdout);
scanf("%lld%lld",&n,&k);
if(!(k=(1<<n)-k)) return puts("-1"),0;
fo(i,0,n)
{
C[i][0]=1;
fo(j,1,i) C[i][j]=C[i-1][j]+C[i-1][j-1];
}
fo(i,0,n) fo(j,i+1,n) C[i][j]=1;
for(x=0;k>C[n][x];k-=C[n][x++]);
fo(i,1,n)
{
if(k>C[n-i][x-1]) {k-=C[n-i][x-1]; continue;}
x--; a[n-i]=1;
}
x=0;
fo(i,0,n-1)
{
x+=a[i];
fo(j,1,x) printf("1 ");
fo(j,j,n) printf("0 ");
puts("");
}
}