如此简单的DP我居然没有想出来,我好菜啊!!!
小视野链接
洛谷链接
题目大意
在一个长度为 n n 序列上每格涂颜色(共三种颜色),给出个三元组 (xi,yi,zi) ( x i , y i , z i ) 表示要求在区间 xi x i ~ yi y i 中颜色种类等于 zi z i ,求方案数。
题解
DP
然而DP的状态有点妙,我这个菜鸡根本没想到
设
f[i][j][k]
f
[
i
]
[
j
]
[
k
]
表示当前填到第
i
i
格,除了当前填的颜色的另外两种颜色分别填在第格和第
k
k
格(保证,若
j
j
或等于0代表未填到)。先不管要求,直接暴力转移(具体转移见代码注释),最后把不合要求的状态置0即可。
三种颜色是不同的,但上述DP没有考虑具体填哪一种颜色,所以统计答案时要乘上一些东西。
代码
我用了滚动数组。。
#pragma GCC optimize(3)
#include<iostream>
#include<iomanip>
#include<stack>
#include<queue>
#include<list>
#include<vector>
#include<set>
#include<map>
#include<string>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<ctime>
#define ll unsigned long long
#define db double
#define inf 301
#define INF (int)1e8
#define mod (int)(1e9+7)
#define pi acos(-1)
#define rd(n) {n=0;char ch;int f=0;do{ch=getchar();if(ch=='-'){f=1;}}while(ch<'0'||ch>'9');while('0'<=ch&&ch<='9'){n=(n<<1)+(n<<3)+ch-48;ch=getchar();}if(f)n=-n;}
using namespace std;
typedef pair<int,int> P;
int n,m;
vector <P> a[inf];
int f[2][inf][inf];
int main(){
rd(n) rd(m)
int x,y,z;
for (int i=1;i<=m;i++){
rd(x) rd(y) rd(z)
if (z>y-x+1){
puts("0");
return 0;
}
a[y].push_back(make_pair(x,z));
}
int now=0;
f[0][0][0]=1;
for (int i=1;i<n;i++){
now^=1;
memset(f[now],0,sizeof(f[now]));
for (int j=0;j<=i;j++){
for (int k=j;k<=i;k++){
f[now][j][k]=(f[now][j][k]+f[now^1][j][k])%mod;
//选和第i-1格一样的颜色
if (j==0 && k==0){
f[now][j][i]=(f[now][j][i]+f[now^1][j][k])%mod;
//特判
}
else{
f[now][j][i]=(f[now][j][i]+f[now^1][j][k])%mod;
//选和第k格一样的颜色
f[now][k][i]=(f[now][k][i]+f[now^1][j][k])%mod;
//选和第j格一样的颜色
}
}
}
if (i==2){
i=2;
}
for (int pos=0;pos<a[i+1].size();pos++){
int l=a[i+1][pos].first;
for (int j=0;j<=i;j++){
for (int k=j;k<=i;k++){
int cnt=1+(j>=l)+(k>=l);
if (cnt!=a[i+1][pos].second){
f[now][j][k]=0;
}
}
}
}
}
ll ans=0;
for (int j=0;j<n;j++){
for (int k=j;k<n;k++){
if (!j && !k){
ans=(ans+((ll)f[now][j][k])*3LL%mod)%mod;
}
else{
ans=(ans+((ll)f[now][j][k])*6LL%mod)%mod;
}
}
}
printf("%lld\n",ans);
}