A.Blank
题意:给你长度为 得数组,往里面填数 {0,1,2,3} 四个数字,有 m 个约束条件,第i个约束条件是:区间 有 种不同的数,问一共有多少种满足约束条件的序列。
题解:
分析:
我们可以用 dp(i,p1,p2,p3,p4) 表示第 i 个位置 {0,1,2,3}对应出现的最后一个位置,对于第 i 个约束条件,把不满足的 × 掉就行了,状态方程也很好写
但是存在一个致命的问题,上式时间复杂度 O(n) ;滚动数组优化之后,空间复杂度为 O(n)
两个都不行,因此我们需要优化上述状态。
经过观察发现,{0,1,2,3} 的顺序对于约束条件并没什么作用,只是方便状态转移,并且对于第 i 个位置来讲,一定会存在该位置 i,因此我们可以将 {0,1,2,3} 最后一次出现的位置从小到达排序,设为 ( p1,p2,p3,i ); i 可以忽略掉不写,然后就是思考怎么进行状态转移了.
设第 i-1 个状态 p1,p2,p3,i-1 (p1 < p2 <p3 <i-1)
因为是{0,1,2,3}无序,我们直接考虑 第 i 位填的数字会对应替换掉哪一位
则有以下四个状态:
p2,p3,i-1,i
p1,p3,i-1,i
p1,p2,i-1,i
p1,p2,p3,i
注:因为 i 随着改变,不用在dp方程里写出来,我上面只是为了更加方便理解
则状态方程为:
注:上述转移方程第 1 项的那个 i-1 和 i 只是代表他们是第几个状态,是要用滚动数组存的
现在是如何加上限制条件:区间 有 种不同的数
dp(i,p1,p2,p3)表示的是{0,1,2,3}出现的位置排序后为 p1,p2,p3,i
因此我们可以先将 限制条件按 右区间 从小到大排序,处理第 i 个位置的时候,如果 i == r[x],我们将O(n)枚举p1,p2,p3就行了,将不满足的置0.
时间复杂度:O(n) , 空间复杂度为 O(n)
/*
卡常数,没写好就超时,心态爆炸
900+ ms
*/
#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int N = 100 + 5;
const int mod=998244353;
int dp[2][N][N][N];
vector<vector<pair<int,int> > >V(N);
void inline slove(){
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
V[i].clear();
for(int i=1;i<=m;i++)
{
int l,r,x;
scanf("%d%d%d",&l,&r,&x);
V[r].push_back(make_pair(l,x));
}
memset(dp,0,sizeof dp);
dp[0][0][0][0]=1;
for(int i=1;i<=n;i++) //枚举第 i 位填什么
{
int t1=i&1,t2=(i-1)&1;
for(int j=0;j<=i;j++)
for(int k=0;k<=i;k++)
for(int z=0;z<=i;z++)
dp[t1][j][k][z]=0;
for(int p1=0;p1<i;p1++)
for(int p2=p1;p2<=i;p2++)
for(int p3=p2;p3<=i;p3++)
{
dp[t1][p2][p3][i-1]=(dp[t1][p2][p3][i-1]+dp[t2][p1][p2][p3])%mod;
dp[t1][p1][p3][i-1]=(dp[t1][p1][p3][i-1]+dp[t2][p1][p2][p3])%mod;
dp[t1][p1][p2][i-1]=(dp[t1][p1][p2][i-1]+dp[t2][p1][p2][p3])%mod;
dp[t1][p1][p2][p3] =(dp[t1][p1][p2][p3] +dp[t2][p1][p2][p3])%mod;
}
//检查
for(int p1=0;p1<i;p1++)
for(int p2=p1;p2<i;p2++)
for(int p3=p2;p3<i;p3++)
for(int z=0;z<V[i].size();z++)
{
int x= (i >=V[i][z].first);
x+=(p1>=V[i][z].first);
x+=(p2>=V[i][z].first);
x+=(p3>=V[i][z].first);
if(x!=V[i][z].second)
dp[t1][p1][p2][p3]=0; //当前状态清0
}
}
LL ans=0;
for(int p1=0;p1<=n;p1++)
for(int p2=p1;p2<=n;p2++)
for(int p3=p2;p3<=n;p3++)
ans=ans+dp[n&1][p1][p2][p3];
printf("%lld\n",ans%mod);
}
int main() {
int t;
scanf("%d",&t);
while(t--)
slove();
}