P7690 [CEOI2002] A decorative fence 题解

题目传送门

前置知识

计数 DP

解法

方案数统计同 luogu P2467 [SDOI2010] 地精部落,但部分写得不太好看的状态转移方程在本题中并不适用,但仍可借鉴其“离散化”思想。

考虑试填。

\(f_{i,j,0/1}\) 表示用 \(i\) 块不同的木板构成栅栏,其中最左边的木板的长度从小到大排在第 \(j\) 位(仅是相对大小关系),处于低位/高位的方案数,状态转移方程为 \(\begin{cases} f_{i,j,0}=\sum\limits_{k=j}^{i-1}f_{i-1,k,1} \\ f_{i,j,1}=\sum\limits_{k=1}^{j-1}f_{i-1,k,0} \end {cases}\),边界为 \(f_{1,1,0/1}=1\)

特别处理第 \(1\) 块木板的长度和低/高位情况 \(k\)。接着枚举每位的实际长度 \(j\) 和排名 \(rk\) 使其符合排名即可。

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long 
#define ull unsigned long long
#define sort stable_sort 
#define endl '\n'
ll f[25][25][2],vis[25];
int main()
{
	ll t,n,c,ans,rk,i,j,k,h;
	cin>>t;
	f[1][1][0]=f[1][1][1]=1;
	for(i=2;i<=20;i++)
	{
		for(j=1;j<=i;j++)
		{
			for(k=j;k<=i-1;k++)
			{
				f[i][j][0]+=f[i-1][k][1];
			}
			for(k=1;k<=j-1;k++)
			{
				f[i][j][1]+=f[i-1][k][0];
			}
		}
	}
	for(h=1;h<=t;h++)
	{
		cin>>n>>c;
		k=ans=-1;
		memset(vis,0,sizeof(vis));
		for(i=1;i<=n;i++)
		{
			if(f[n][i][1]>=c)
			{
				ans=i;
				k=1;
				break;
			}
			else
			{
				c-=f[n][i][1]; 
			}
			if(f[n][i][0]>=c)
			{
				ans=i;
				k=0;
				break;
			}
			else
			{
				c-=f[n][i][0];
			}
		}
		vis[ans]=1;
		cout<<ans<<" ";
		for(i=2;i<=n;i++)
		{
			k^=1;
			rk=0;
			for(j=1;j<=n;j++)
			{
				if(vis[j]==0)
				{
					rk++;
					if((k==0&&j<ans)||(k==1&&j>ans))
					{
						if(f[n-i+1][rk][k]>=c)
						{
							ans=j;
							break;
						}
						else
						{
							c-=f[n-i+1][rk][k];
						}
					}
				}
			}
			vis[ans]=1;
			cout<<ans<<" ";
		}
		cout<<endl;
	}
	return 0;
}
posted @ 2024-07-04 08:24  hzoi_Shadow  阅读(6)  评论(0编辑  收藏  举报
扩大
缩小