状态f[i][j][k][l]表示前i个数,四种数的最后一次出现的位置分别是i、j、k和l(i>j>k>l),判断所有第右端点为i的区间是否满足此要求(不满足重置为0),考虑第i+1个位置填什么,转移到下一个位置上即可。
这样的时间复杂度看似是$o(Tn^{4})$,实际上由于枚举只需要比上一个数小就行了,还要除以24;空间复杂度通过滚动可以压到$o(n^{3})$,可以卡过去。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define mod 998244353 4 int t,n,m,x,y,z,ans,ll[105][5],rr[105][5],f[2][105][105][105]; 5 void add(int &x,int y){ 6 x+=y; 7 if (x>=mod)x-=mod; 8 } 9 int calc(int a,int b,int c,int d,int e){ 10 return (e<=a)+(e<=b)+(e<=c)+(e<=d); 11 } 12 int main(){ 13 scanf("%d",&t); 14 while (t--){ 15 scanf("%d%d",&n,&m); 16 memset(ll,0x3f,sizeof(ll)); 17 memset(rr,-1,sizeof(rr)); 18 for(int i=1;i<=m;i++){ 19 scanf("%d%d%d",&x,&y,&z); 20 ll[y][z]=min(ll[y][z],x); 21 rr[y][z]=max(rr[y][z],x); 22 } 23 memset(f,0,sizeof(f)); 24 f[0][0][0][0]=1; 25 ans=0; 26 for(int i=1,p=1;i<=n;i++,p^=1){ 27 for(int j=0;(!j)||(j<i);j++) 28 for(int k=0;(!k)||(k<j);k++) 29 for(int l=0;(!l)||(l<k);l++){ 30 x=f[p^1][j][k][l]; 31 if (x){ 32 add(f[p][j][k][l],x); 33 add(f[p][i-1][k][l],x); 34 add(f[p][i-1][j][l],x); 35 add(f[p][i-1][j][k],x); 36 } 37 f[p^1][j][k][l]=0; 38 } 39 for(int j=0;j<i;j++) 40 for(int k=0;(!k)||(k<j);k++) 41 for(int l=0;(!l)||(l<k);l++){ 42 for(int q=1;q<=4;q++) 43 if ((calc(i,j,k,l,ll[i][q])>q)||(calc(i,j,k,l,rr[i][q])<q)){ 44 f[p][j][k][l]=0; 45 break; 46 } 47 if (i==n)add(ans,f[p][j][k][l]); 48 } 49 } 50 printf("%d\n",ans); 51 } 52 }