2023 Hubei Provincial Collegiate Programming Contest(gym104337)E. Inverse Counting Path

題目大意

構造一個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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章