一道神仙$DP$题$QwQ$。继续抄题解
设$f(i,j,k)$表示已经处理前$i$个元素、最后一次出现的位置第二靠前的颜色位置为$j$、第三靠前的位置为$k$时的方案数。显然第一靠前的是$i$,所以不用设出来。
对于每个限制条件,把它限制在右端点。(右端点是临界情况,右端点之前没达到条件可以在后面补足)
用刷表法转移。枚举第$i+1$个元素的颜色并检验每个限制$i+1$的条件是否满足,满足就转移。
具体来说分$3$种情况:
颜色和$i$一样:$f(i+1,j,k)+=f(i,j,k)$
颜色和$j$一样:$f(i+1,i,k)+=f(i,j,k)$
颜色和$k$一样:$f(i+1,i,j)+=f(i,j,k)$
初始状态:$f(1,0,0)=1$
最后答案为$3*\sum\limits_{i=0}^{n-1}\sum\limits_{j=0}^{\max(i-1,0)}f(n,i,j)$
为啥要乘$3$呢?这里$i,j,k$只是个位置关系,并不是具体某种颜色,所以任意给$i,j,k$选颜色,有$3$种排列。
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <vector>
#define maxn 305
#define inf 0x3f3f3f3f
const int mod = 1e9+7;
using namespace std;
inline int read(){
int x=0,y=0;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')y=1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
return y?-x:x;
}
#define in(l,r,x) (l<=x&&x<=r)
vector<int>limit[maxn];//限制条件
int l[maxn],c[maxn],f[maxn][maxn][maxn];
inline int count(int l,int r,int c1,int c2,int c3){
return in(l,r,c1)+in(l,r,c2)+in(l,r,c3);
}//计算区间为l,r、颜色位置为c1,c2,c3时,有多少种颜色出现在区间内
int main(){
int n=read(),m=read(),k1,k2,k3,x,ans=0;
for(register int i=1;i<=m;++i)
l[i]=read(),limit[read()].push_back(i),c[i]=read();
if(n==1){
ans=3;
for(register int i=1;i<=m;++i)
if(c[i]==1)ans=min(ans,1);
else ans=0;
printf("%d\n",ans);
return 0;
}//要特判n=1,刷表法刷不到1
f[1][0][0]=1;
for(register int c1=1;c1<n;++c1)
for(register int c2=0;c2<c1;++c2)
for(register int c3=0;c3<=max(c2-1,0);++c3){
bool ok1=1,ok2=1,ok3=1;//选1、2、3颜色是否可行
for(register int j=0;j<limit[c1+1].size();++j){
x=limit[c1+1][j];
k1=count(l[x],c1+1,c1+1,c2,c3),k2=count(l[x],c1+1,c1,c1+1,c3),k3=count(l[x],c1+1,c1,c2,c1+1);
ok1&=(k1==c[x]),ok2&=(k2==c[x]),ok3&=(k3==c[x]);
}//依次检验每个区间
if(ok1)(f[c1+1][c2][c3]+=f[c1][c2][c3])%=mod;
if(ok2)(f[c1+1][c1][c3]+=f[c1][c2][c3])%=mod;
if(ok3)(f[c1+1][c1][c2]+=f[c1][c2][c3])%=mod;
}
for(register int j=0;j<n;++j)
for(register int k=0;k<=max(j-1,0);++k)
(ans+=f[n][j][k])%=mod;
printf("%d\n",((ans<<1)%mod+ans)%mod);//我就不开long long QwQ
}