多校第一场第一题,这种类型的dp之前做过两题,状态转移一般是从当前状态往后推的
很经典的dp,不过很卡时间
/* 定义 dp[t][i][j][k]代表填完前 t 个位置后,{0, 1, 2, 3} 这 4 个数字最后一次出现的位置, 排序后为 t, i, j, k(t > i > j > k) 的方案数目,则按照第 t 位的数字的四种选择,可以得 到四种转移。 t选t-1这个位置的数:dp[t][i][j][k] t选i这个位置的数:dp[t][t-1][j][k] t选j这个位置的数:dp[t][t-1][i][k] t选k这个位置的数:dp[t][t-1][i][j] 枚举r[l]==t+1的所有条件,当且仅当满足所有条件时才进行转移 最后的方案数=sum{dp[n]} 总时间复杂度 O(n4)。滚动一维,空间复杂度 O(n3) */ #include<bits/stdc++.h> using namespace std; #define maxn 110 #define ll long long #define mod 998244353 ll dp[2][maxn][maxn][maxn]; int n,m; struct Node{ int l,x; Node(){} Node(int l,int x):l(l),x(x){} }; vector<Node>v[maxn]; inline void update(ll &a,ll b){ a=(a+b); while(a>=mod)a-=mod; } int c; void solve(){ c=0; dp[c][0][0][0]=1; for(int t=1;t<=n;t++){ c^=1; for(int i=0;i<=t;i++) for(int j=0;j<=i;j++) for(int k=0;k<=j;k++) dp[c][i][j][k]=0; for(int i=0;i<t;i++) for(int j=0;j<=i;j++) for(int k=0;k<=j;k++){ update(dp[c][i][j][k],dp[c^1][i][j][k]); update(dp[c][t-1][j][k],dp[c^1][i][j][k]); update(dp[c][t-1][i][k],dp[c^1][i][j][k]); update(dp[c][t-1][i][j],dp[c^1][i][j][k]); } for(int p=0;p<v[t].size();p++){//枚举每个条件 int l=v[t][p].l,x=v[t][p].x; for(int i=0;i<t;i++) for(int j=0;j<=i;j++) for(int k=0;k<=j;k++){ int cnt=1; if(i>=l)cnt++; if(j>=l)cnt++; if(k>=l)cnt++; if(cnt!=x)dp[c][i][j][k]=0; } } } } int main(){ //ios::sync_with_stdio(false); int t;cin>>t; while(t--){ for(int i=0;i<maxn;i++)v[i].clear(); scanf("%d%d",&n,&m); for(int i=1;i<=m;i++){ int l,r,x; scanf("%d%d%d",&l,&r,&x); v[r].push_back(Node(l,x)); } solve(); ll ans=0; for(int i=0;i<n;i++) for(int j=0;j<=i;j++) for(int k=0;k<=j;k++) ans+=dp[c][i][j][k],ans%=mod; cout<<ans<<' '; } }