题解 CF567F 【Mausoleum】
从放第一本书开始推,每次放两本相同高度的,设 \(f_{i,j}\) 为左边放i本书,右边放j本书的方案,放书的时候判断一下是否合法,如果合法,\(f_{i,j}\) += \(f_{i-2,j}\) , \(f_{i-1,j-1}\) , \(f_{i,j-2}\) ,这个方程应该不难理解**
至于条件可以用邻接表,注意一下邻接表的数据范围,因为书的位置有200个,正反都存一遍就好了
符号的正反:注意与 >= 相对的是 <= , 不是 <**
这是我用了 N(N>20) 次调试换来的······
得到的结果要除以3,因为当放最后两本书时,\(f_{i-2,j} == f_{i-1,j-1} == f_{i,j-2}\)
具体见代码:
#include<bits/stdc++.h>
using namespace std;
long long n,f[210][210],tot,k,ans;
long long head[210],next11[410],ver[410],edge[410];
//一定开long long,一定开long long,一定开long long
void add(long long x,long long y,long long z){
next11[++tot]=head[x],ver[tot]=y,edge[tot]=z,head[x]=tot;
}//邻接表
bool spfa(long long l,long long r,long long ll,long long rr){
//寻找出边,l表示放的两本书中左边的那本,r是右边那本,
//ll表示没有放书的区间的左端点,rr表示右端点,
//可知 ll~rr 中的书高度都大于将要放的两本书
for(int i=head[l];i;i=next11[i])
{
int y=ver[i],z=edge[i];
if(y==r){
if(z==2||z==4) return false;
}//特判,等于的情况只可能:出边为放的另一本书的位置
else if(z<=2) {
if(y<ll||y>rr) return false;
}
else if(z>=4) {
if(y>ll&&y<rr) return false;
}
else if(z==3) return false;
}//寻找与左端点有关的条件
for(long long i=head[r];i;i=next11[i])
{
long long y=ver[i],z=edge[i];
if(y==l){
if(z==2||z==4) return false;
}
else if(z<=2) {
if(y<ll||y>rr) return false;
}
else if(z>=4) {
if(y>ll&&y<rr) return false;
}
else if(z==3) return false;
}//同上
return true;
}
int main(){
cin>>n>>k;
for(int i=1;i<=k;i++)
{
long long x,y,z=0;
string c;
cin>>x>>c>>y;
if(c[1]=='=')
{
if(c[0]=='<') z=1;
else z=5;
}
else {
if(c[0]=='=') z=3;
if(c[0]=='<') z=2;
if(c[0]=='>') z=4;
}
if(x==y)
{
if(z==2||z==4)
{
cout<<0;
return 0;
}
else continue;
}
add(x,y,z);
add(y,x,6-z);//懂我为什么强调了吧
}
f[0][0]=1;
for(int i=1;i<=n;i++)
{
for(int j=0;j<=2*i;j++)
{
if(j>=2&&(k==0||spfa(j-1 , j , j+1 , 2*n-2*i+j)))
f[j][2*i-j]+=f[j-2][2*i-j];
if(j>=1&&2*i-j>=1&&(k==0||spfa( j , 2*n-2*i+j+1 , j+1 , 2*n-2*i+j)))
f[j][2*i-j]+=f[j-1][2*i-j-1];
if(2*i-j>=2&&(k==0||spfa(2*n-2*i+j+1 , 2*n-2*i+j+2 , j+1 , 2*n-2*i+j)))
f[j][2*i-j]+=f[j][2*i-j-2];
}//自行理解,最好画个图
}
for(int i=0;i<=2*n;i++) ans+=f[i][2*n-i];//ans累加,
cout<<ans/3;//重要
return 0;
}