題意
總共有個編號,每個編號可以填充一個數字,分別爲。有個條件,每個條件組成爲表示在區間內的不同數字的個數爲個。
問總共有多少種填充的方式,結果對998244353取模。
題解
定義表示從前向後,當填充到第個格子的時候,4個數字最後出現的位置分別爲時的方案數。其中。最初始的狀態,在爲0時取等號,表示目前所有的數字都沒有出現。
數字的種類是不重要的,因爲計算的過程已經把不同的數字組合方式計算進去了。重要的在填充第個格子的時候從第的方案轉移過來。
填充當前格子的時候,我們有四種數字的一個
如果我們填充的數字和位置的數字是一樣的,那麼轉移狀態爲
所以dp轉移方式爲
同樣的方式,如果取的數字和位置的數字是一樣的。那麼
類比如下
然後對於每一個以爲右端點的區間,枚舉狀態檢查是否滿足條件。即每個編號的最後一次出現的時候是否在區間內,然後判斷要求區間內的編號數和要求的編號數是否相同。
由於第4維是原始空間,則會爆掉規定內存,所以需要用滾動數組來優化內存。
#include <iostream>
#include <algorithm>
#include <vector>
#include <cstdio>
#include <cstring>
using namespace std;
const int mod = 998244353;
int T;
int n,m;
long long dp[110][110][110][2];
struct Position {
int L;
int num ;
};
vector<Position> p[110];
int main()
{
scanf("%d", &T);
while(T--) {
scanf("%d %d", &n, &m);
for(int i = 0;i<110;i++) p[i].clear();
for(int i = 0,l, r, x ;i< m;i++) {
scanf("%d %d %d", &l, &r, &x);
p[r].push_back({l, x});
}
dp[0][0][0][0] = 1;
int cur = 1,pre = 0;
for(int i = 1;i<=n;i++) {
for(int z = 0;z<=i;z++) {
for(int y = 0;y<=z;y++) {
for(int x = 0;x<=y;x++) {
dp[x][y][z][cur] = 0;
}
}
}
for(int z = 0;z<i;z++) {
for(int y = 0;y<=z;y++) {
for(int x = 0; x<=y;x++) {
long long temp = dp[x][y][z][pre];
dp[x][y][z][cur] += temp ;
dp[y][z][i-1][cur] += temp;
dp[x][z][i-1][cur] += temp;
dp[x][y][i-1][cur] += temp;
dp[x][y][z][cur] %= mod ;
dp[y][z][i-1][cur] %= mod;
dp[x][z][i-1][cur] %= mod;
dp[x][y][i-1][cur] %= mod;
}
}
}
for(unsigned int pp = 0;pp < p[i].size(); pp++) {
for( int z = 0;z< i;z++) {
for(int y = 0;y<=z;y++) {
for(int x = 0;x<=y;x++) {
int temp = 1 + (z >= p[i][pp].L ? 1:0)
+ (y >= p[i][pp].L ? 1:0)
+ (x >= p[i][pp].L ? 1:0);
if(temp != p[i][pp].num) {
dp[x][y][z][cur] = 0;
}
}
}
}
}
swap(pre, cur);
}
long long ans = 0;
for (int z = 0;z<n;z++) {
for(int y = 0;y<=z;y++) {
for(int x = 0;x<=y;x++){
ans += dp[x][y][z][pre];
ans %= mod;
}
}
}
printf("%lld\n", ans);
}
return 0;
}