http://acm.hdu.edu.cn/showproblem.php?pid=6578
题目描述
There are N blanks arranged in a row. The blanks are numbered 1,2,…,N from left to right.
Tom is filling each blank with one number in {0,1,2,3}. According to his thought, the following M conditions must all be satisfied. The ith condition is:
There are exactly xi different numbers among blanks ∈[li,ri].
In how many ways can the blanks be filled to satisfy all the conditions? Find the answer modulo 998244353.
输入
The first line of the input contains an integer T(1≤T≤15), denoting the number of test cases.
In each test case, there are two integers n(1≤n≤100) and m(0≤m≤100) in the first line, denoting the number of blanks and the number of conditions.
For the following m lines, each line contains three integers l,r and x, denoting a condition(1≤l≤r≤n, 1≤x≤4).
输出
For each testcase, output a single line containing an integer, denoting the number of ways to paint the blanks satisfying all the conditions modulo 998244353.
复制样例数据
2
1 0
4 1
1 3 3
样例输出
4
96
T组样例,
让你求长度为n的序列种数(有m个限制,在[li,ri]区间内要有且仅有xi种数)pos
朴素想法,dp[len][p0][p1][p2][p3],长度为len的序列,最后一个0的下标是p0,最后一个1的下标是p1,……
先不考虑时间问题,很明显内存不够,1e10
长度为len的序列肯定是从长度为len-1的序列推过来的,所以第一维可以01滚动
另外,既然是长度为len的序列,那么p0,p1,p2,p3中必有一个是len,我们可以考虑将其与第一维合并(共用),但哪个数的下标是len呢,很迷~~~
很明显,每个p实际上不止代表着下标,也代表这种数(除了没用这个数的时候都是0,用了的话最后一次出现的下标都是唯一的,不可能一个位置有两种数),也就是说有几个p在限制范围内,该范围内就有几种数。与至于是pi大还是pj无关,那么我们可以把4个p按非递增顺序排个序,排完序后一样的看做一种状态
定义dp[i][j][k][t]为填完前i个位置后,{0,1,2,3}这4个数字最后一次出现的位置,排序后为i,j,k,t(i>=j>=k>=t),枚举第i位数字是由谁转移过来的
dp[i][j][k][t]+=dp[i-1][j][k][t](下标为i的位置,放的数和最后一次出现位置的下标为i-1位置的数一样)
dp[i][i-1][k][t]+=dp[i-1][j][k][t](下标为i的位置,放的数和最后一次出现位置的下标为j位置的数一样)
dp[i][i-1][j][t]+=dp[i-1][j][k][t](下标为i的位置,放的数和最后一次出现位置的下标为k位置的数一样)
dp[i][i-1][j][k]+=dp[i-1][j][k][t](下标为i的位置,放的数和最后一次出现位置的下标为t位置的数一样)
然后把第一维01滚动,空间优化成了2*1e6,时间复杂度也从O(n^5)优化到了O(n^4)
这是没有限制的情况,有限制怎么办?加判断!
一个下标代表一种数,对于每个限制区间右端点等于i的限制,
if((1+(j>=lim[pos].l)+(k>=lim[pos].l)+(t>=lim[pos].l))!=lim[pos].x)
dp[p][j][k][t]=0;
不符合限制,那就不能给后面提供贡献,就是0
对于限制区间右端点为i区间存的时候要处理好,可以用个vector存,v[i]储存右端点为i的所有区间,这样判断的时候能省很多时间
struct node{
int l,r,x;
}lim[105];
int v[105][105],tail[105];
我用二维数组模拟的vector,v[i]储存右端点下标为i的区间在lim中的下标,这样下标就能跳着找了,不用for循环1~m了
(这里排不排序没有影响,反正该判断的区间都得判断,跑不了,嘤嘤嘤)
#include
using namespace std;
typedef long long ll;
const int mod=998244353;
int dp[2][101][101][101];
struct node{
int l,r,x;
}lim[105];
//bool cmp(node a,node b){
// if(a.r!=b.r) return a.r=lim[pos].l)+(k>=lim[pos].l)+(t>=lim[pos].l))!=lim[pos].x)
dp[p][j][k][t]=0;
}
}
}
}
}
ll ans=0;
for(int i=0;i