题目大意: 给你
~
这些数,每个数两个,要求构造一个形如一段不降+一段不升
的序列,并且满足若干个形如
x
的限制,其中 x 可以是
中的一个,意思是
位置上数与
位置上的数的大小关系,问有多少个满足要求的序列。
题解
这题如何定义 数组是个难点,如果想到了优秀的定义那么就不难做了。
因为这个序列是单峰的,于是可以设 表示往左边放了 个数,往右边放了 个数的方案数。
然后考虑将 ~ 这些数从小到大一对一对放进去,要么两个都放在左边,要么一个放左边,一个放右边,要么就都放右边,那么转移的时候考虑从这三个状态转移即可。
然后转移的时候顺便判断一下是否满足限制即可,此时去判断是很容易的,因为对于一个状态 ,它实际上已经填充了序列中 ~ 和 ~ 这些位置,而这些已经填充的位置绝对比我们要放进来的这对数要小,而剩下的没放的位置上将要放的数肯定比我们要放的数要大。
细节:
- 限制我用邻接表存
- 答案要除以 因为对于最后还剩一对数没放的状态,它一定可以对三个放满的状态产生贡献,所以要去重
代码如下:
#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);
}