Educational Codeforces Round 80 (Rated for Div. 2)
C. Two Arrays
題意
題目可以轉換成求一個長度為2m的不遞減陣列,元素取值為1-n;求得方案數
思想
可以考慮將a,b陣列連起來形成一個b[1],b[2],…,b[m],a[m],a[m-1],…,a[1]的陣列,長度為2m且非嚴格遞減,取值都在[1,n]之間(與原問題等價),就相當於有n個盒子排成一排,由他們的順序決定所取的數字(即n個不同的盒子),然後將2m個相同的小球放進去,允許空盒子存在(將這些小球放的盒子標號從大到小連起來就組成了一個滿足條件的陣列),由隔板法得方案數為\(C({2m+n-1},{n-1})\)
小球組合
組合數學
程式碼
包含求逆元和求階乘的技巧
inline int read() { char c=getchar();int x=0,f=1; while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();} return x*f; } int fac[2001]; int inv[2001]; int sum1[1001]; int sum2[1001]; const int p=1e9+7; int qpow(int a,int b) { if(b==0)return 1; if(b%2==0) { int x=qpow(a,b/2)%p; return x*x%p; } return a*qpow(a,b-1); } void getinv() { fac[0]=inv[0]=1; for(int i=1;i<=2000;i++) { fac[i]=fac[i-1]*i%p; inv[i]=qpow(fac[i],p-2)%p; } } int getC(int n,int m) { return fac[n]*inv[n-m]%p*inv[m]%p; } main(void) { int n=read(); int m=read(); int x=2*m+n-1; //c(n-1,2*m+n-1) getinv(); cout<<getC(x,n-1); }
D. Minimax Problem
題目大意
給出一個n*m的矩陣,我們用maze[n][m]來表示每一個元素,現在我們需要選出其中 i 和 j 兩行,i 和 j 可以相同,用這兩行的元素構成一個新的陣列a,構造規則為a[k]=max(maze[i][k],maze[j][k]),現在我們要使陣列a中的最小值最大,請問該如何選擇 i 和 j 才能滿足條件
題目分析
讀完題目後感覺亂糟糟的,但靜下心來分析一下,要求最小值的最大值,顯然的二分,所以我們很直接的確定下來要對於陣列a的最小值進行二分了,但check函式該怎麼寫是我們接下來需要考慮的問題了,其實當我們確定下來了最小值之後,就相當於對原矩陣構成了一種約束條件,換句話說,原矩陣中值小於當前限制的元素,我們是無法選擇的了,不然最後構成的陣列a中的最小值就無法保證為當前二分的答案了,有了這樣一個轉換後,我們就可以將原矩陣中大於當前二分的答案的位置都置為1,其餘位置置為0,這樣就很自然的進行了狀態壓縮,因為每一行的元素都是繫結的,也就是對於某一行來說,我們只能選擇或者不選,這樣又是一種標準的列舉子集,我們再看一眼資料範圍,發現m最大隻有7,我們如果以列舉子集的形式來確定 i 和 j 的話,時間複雜度最大也只有2^14,所以我們可以直接兩層for迴圈列舉子集狀態,找到一種 i 和 j 的情況,滿足其能覆蓋整個子集就好了,具體實現看程式碼吧,程式碼寫的比較清楚了
程式碼
const int inf=1e9; inline int read() { char c=getchar();int x=0,f=1; while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();} return x*f; } int mp[300005][10]; int vis[100000]; int ans1,ans2,ans; int n,m; bool check(int x) { memset(vis,0,sizeof(vis)); for(int i=1;i<=n;i++) { int val=0; for(int j=1;j<=m;j++) { if(mp[i][j]>=x) val|=1<<(j-1); } vis[val]=i; } for(int i=0;i<(1<<m);i++) { if(vis[i]) { for(int j=0;j<(1<<m);j++) { if(vis[j]) { if((i|j)==(1<<m)-1) { if(ans<=x) { ans1=vis[i]; ans2=vis[j]; ans=x; } return true; } } } } } return false; } main(void) { n=read(); m=read(); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { mp[i][j]=read(); } int l=0; int r=inf; while(l<=r) { int mid=(l+r)>>1; if(check(mid)) { l=mid+1; } else { r=mid-1; } } printf("%d %d\n",ans1,ans2); }