題目大意
構造一個01網格圖,1能走0不能走
使得從左上走到右下(只能走右或走下)的方案數恰好爲x
n<=30,x<=1e9
題解
類似於進制轉換,假設能把x寫成\(x=\sum a_ip_i\),\(p_i\)表示第i位一個1代表的值,然後再獨立構造求和
構造需要滿足的條件:
①可以實現對單個\(p_i\)乘以\(a_i\)
②可以把\(a_ip_i\)求和
要求每一小部分都是獨立的,不能有格子碰在一起
先這樣構造1的塊(數字是這樣構造時的路徑數),設每3行的右上角的數爲p[i]:
1
10
74
549
4177
32568
259272
2100064
17257108
143537134
這樣構造:
將畫線的部分設爲1,這樣原始部分的方案不會改變,同時可以實現①\(a_i*p_i\)和②\(a_ip_i\)求和
(設p0=1,p1=10,上圖就是\(3*p_0+2*p_1\)
按照這樣連線 ,在最後一行連一豎來求和即可
如何求ai:從高到低求,如果x>=當前p[i]就減p[i],a[i]+1(也類似進制轉換)
(也可以理解成讓ai儘量小,所以一次減的數儘量大,每次減最大的能減的數
code
#include <bits/stdc++.h>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define ll long long
//#define file
using namespace std;
int n=30,x,i,j,k,l,id,X,Y;
int ans[31][31];
int f[11]={0,1,10,74,549,4177,32568,259272,2100064,17257108,143537134};
int v[11];
void init()
{
fo(i,1,n)
{
l=(i-1)/3+3;
fo(j,1,l)
ans[i][j]=1;
ans[i][n]=1;
}
}
int main()
{
#ifdef file
freopen("E.in","r",stdin);
#endif
init();
scanf("%d",&x);
fd(id,10,1)
{
while (x>=f[id])
{
x-=f[id];
++v[id];
}
if (v[id])
{
X=id*3-2;
Y=id+2;
fo(j,Y,n) ans[X][j]=1;
fo(j,n-v[id]+1,n) ans[X][j]=ans[X+1][j]=1;
}
}
printf("%d\n",n);
fo(i,1,n)
{
fo(j,1,n)
printf("%d ",ans[i][j]);
printf("\n");
}
fclose(stdin);
fclose(stdout);
return 0;
}