CodeForces 567F Mausoleum 题解

题目传送门

题目大意: 给你 1 1 ~ n n 这些数,每个数两个,要求构造一个形如一段不降+一段不升的序列,并且满足若干个形如 a a x b b 的限制,其中 x 可以是 < , < = , = , > = , > <,<=,=,>=,> 中的一个,意思是 a a 位置上数与 b b 位置上的数的大小关系,问有多少个满足要求的序列。

题解

这题如何定义 d p dp 数组是个难点,如果想到了优秀的定义那么就不难做了。

因为这个序列是单峰的,于是可以设 f [ i ] [ j ] f[i][j] 表示往左边放了 i i 个数,往右边放了 j j 个数的方案数。

然后考虑将 1 1 ~ n n 这些数从小到大一对一对放进去,要么两个都放在左边,要么一个放左边,一个放右边,要么就都放右边,那么转移的时候考虑从这三个状态转移即可。

然后转移的时候顺便判断一下是否满足限制即可,此时去判断是很容易的,因为对于一个状态 f [ i ] [ j ] f[i][j] ,它实际上已经填充了序列中 1 1 ~ i i 2 n j + 1 2n-j+1 ~ 2 n 2n 这些位置,而这些已经填充的位置绝对比我们要放进来的这对数要小,而剩下的没放的位置上将要放的数肯定比我们要放的数要大。

细节:

  1. 限制我用邻接表存
  2. 答案要除以 3 3 因为对于最后还剩一对数没放的状态,它一定可以对三个放满的状态产生贡献,所以要去重

代码如下:

#include <cstdio>
#include <cstring>
#define maxn 1010
#define ll long long

int n,m;
struct node{int y,z,next;};
node e[2*maxn];
int first[maxn];
void buildroad(int x,int y,int z)
{
	static int len=0;
	e[++len]=(node){y,z,first[x]};
	first[x]=len;
}
/*
1:<
2:<=
3:=
4:>=
5:>
*/
bool check(int x,int y,int l,int r)
//表示现在往x,y这两个位置放数,而l~r这个区间是还没有数的
{
	for(int i=first[x];i;i=e[i].next)
	{
		int v=e[i].y,id=e[i].z;
		switch(id)
		{
			case 1: if(v==x||v==y||v<l||v>r)return false; break;
			case 2: if(v!=x&&v!=y&&(v<l||v>r))return false; break;
			case 3: if(v!=x&&v!=y)return false; break;
			case 4: if(v>=l&&v<=r)return false; break;
			case 5: if(v==x||v==y||(v>=l&&v<=r))return false; break;
		}
	}
	for(int i=first[y];i;i=e[i].next)
	{
		int v=e[i].y,id=e[i].z;
		switch(id)
		{
			case 1: if(v==x||v==y||v<l||v>r)return false; break;
			case 2: if(v!=x&&v!=y&&(v<l||v>r))return false; break;
			case 3: if(v!=x&&v!=y)return false; break;
			case 4: if(v>=l&&v<=r)return false; break;
			case 5: if(v==x||v==y||(v>=l&&v<=r))return false; break;
		}
	}
	return true;
}
ll f[maxn][maxn];//f[i][j]表示往左边放了i个数,往右边放了j个数的方案数 

int main()
{
	scanf("%d %d",&n,&m);
	for(int i=1;i<=m;i++)
	{
		int x,y,id;
		char s[5];
		scanf("%d %s %d",&x,s,&y);
		int l=strlen(s);
		if(l==2)
		{
			if(s[0]=='<')id=2;
			else id=4;
		}
		else
		{
			if(s[0]=='<')id=1;
			if(s[0]=='>')id=5;
			if(s[0]=='=')id=3;
		}
		buildroad(x,y,id);
		buildroad(y,x,6-id);
	}
	f[0][0]=1;
	for(int i=1;i<=n;i++)//表示当前放第几对数 
	{
		for(int x=0,y;x<=2*i;x++)
		{
			y=2*i-x;
			if(x-1>0&&(m==0||check(x-1,x,x+1,2*n-y)))f[x][y]+=f[x-2][y];
			if(x>0&&y>0&&(m==0||check(x,2*n-y+1,x+1,2*n-y)))f[x][y]+=f[x-1][y-1];
			if(y-1>0&&(m==0||check(2*n-y+1,2*n-y+2,x+1,2*n-y)))f[x][y]+=f[x][y-2];
		}
	}
	ll ans=0;
	for(int i=0;i<=2*n;i++)
	ans+=f[i][2*n-i];
	printf("%lld",ans/3ll);
}
发布了234 篇原创文章 · 获赞 100 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/a_forever_dream/article/details/102737925