luoguP4229某位歌姬的故事

luoguP4229某位歌姬的故事

题目传送门

分析

套路部分不多说:离散化之后按权值排序,问题就转化成给若干个限制,最大值是 w w w,求方案数。
把区间按右端点排序, f [ i ] [ j ] f[i][j] f[i][j]表示前满足了前 i i i个区间,最后一个最大值位置在 j j j的方案数。
所有合法的 j j j均在 [ L i , R i ] [L_i,R_i] [Li,Ri]之间。
转移的时候枚举当前 j j j之前的最后一个最大值 k k k ( k , j ) (k,j) (k,j)之间填 [ 1 , m ) [1,m) [1,m)的数。
f [ i ] [ j ] = ∑ f [ i − 1 ] [ k ] ⋅ ( m − 1 ) j − k − 1 = ( m − 1 ) j − 1 ∑ f [ i − 1 ] [ k ] ⋅ ( m − 1 ) − k f[i][j]=\sum f[i-1][k] \cdot (m-1)^{j-k-1}=(m-1)^{j-1}\sum f[i-1][k] \cdot (m-1)^{-k} f[i][j]=f[i1][k](m1)jk1=(m1)j1f[i1][k](m1)k$
前缀和优化一下复杂度就能过了。
具体转移的时候由于有离散化还要稍微处理一下。
离散化之后每个 j j j表示的是某个区间内的所有数,为了方便记得把区间右端点 r r r r + 1 r+1 r+1也塞进去离散化好进行区间操作。
具体看代码吧。

代码

#include<bits/stdc++.h>
const int N = 2e3 + 10, P = 998244353;
int ri() {
	char c = getchar(); int x = 0, f = 1; for(;c < '0' || c > '9'; c = getchar()) if(c == '-') f = -1;
	for(;c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) - '0' + c; return x * f;
}
int f[2][N], g[2][N], iv[N], s[N], t[N], L[N], R[N], b[N], val[N], id[N], tp, cnt, n, _n, Q, A;
int fix(int x) {return (x >> 31 & P) + x;}
int add(int a, int b) {return a += b, a >= P ? a - P : a;}
int mul(int a, int b) {return 1LL * a * b % P;}
struct Que {
	int l, r, m;
}q[N];
bool cmp(const Que &a, const Que &b) {
	return a.m == b.m ? a.r < b.r : a.m < b.m;
}
int Pow(int x, int k) {
	int r = 1; k %= P - 1; if(k < 0) k += P - 1;
	for(;k; x = mul(x, x), k >>= 1)
		if(k & 1)
			r = mul(r, x);
	return r;
}
int Dp(int m) {
	if(m == 1) return 1;
	memset(f, 0, sizeof(f));
	memset(g, 0, sizeof(g));
	int c = 0;
	f[0][0] = g[0][0] = 1; iv[0] = 1;
	for(int i = 1;i <= cnt; ++i) {
		g[c][i] = g[c][i - 1];
		s[i] = s[i - 1] + t[i];
		iv[i] = Pow(m - 1, -s[i]);
	}
	for(int i = 1;i <= tp; ++i) {
		c ^= 1;
		for(int j = 0;j < L[i]; ++j)
			f[c][j] = g[c][j] = 0;
		for(int j = L[i]; j <= R[i]; ++j) {
			f[c][j] = f[c ^ 1][j];
			if(j > R[i - 1])
				f[c][j] = add(f[c][j], mul(
				mul(Pow(m - 1, s[R[i - 1]]), Pow(m, s[j - 1] - s[R[i - 1]])),
				mul(g[c ^ 1][R[i - 1]], fix(Pow(m, t[j]) - Pow(m - 1, t[j])))
				));
			g[c][j] = add(g[c][j - 1], mul(f[c][j], iv[j]));
		}
		for(int j = R[i] + 1; j <= cnt; ++j)
			g[c][j] = g[c][j - 1], f[c][j] = 0;
	}
	return mul(g[c][cnt], Pow(m - 1, s[cnt]));
}
int F(int x) {return std::lower_bound(b + 1, b + _n + 1, x) - b;}
int Work() {
	memset(val, 0, sizeof(val));
	n = ri(); Q = ri(); A = ri(); _n = 0;
	for(int i = 1;i <= Q; ++i) {
		int l = ri(), r = ri(), m = ri();
		q[i] = (Que){l, r, m};
		b[++_n] = l; b[++_n] = r;
		if(r != n) b[++_n] = r + 1;
	}
	b[++_n] = 1; b[++_n] = n;
	std::sort(b + 1, b + _n + 1);
	_n = std::unique(b + 1, b + _n + 1) - b - 1;
	std::sort(q + 1, q + Q + 1, cmp);
	for(int i = 1;i <= Q; ++i) {
		q[i].l = F(q[i].l); q[i].r = F(q[i].r);
		bool flag = false; int mx = 0;
		for(int j = q[i].l; j <= q[i].r; ++j)
			if(!val[j])
				val[j] = q[i].m, flag = true;
			else mx = std::max(mx, val[j]);
		if(!flag && mx < q[i].m) return 0;
	}
	int Ans = 1;
	for(int i = 1, j;i <= Q; i = j) {
		for(j = i + 1;j <= Q && q[i].m == q[j].m; ++j) ;
		cnt = 0;
		for(int k = 1;k <= _n; ++k)
			if(val[k] == q[i].m)
				id[k] = ++cnt, t[cnt] = k == _n ?: b[k + 1] - b[k];
		tp = 0;
		for(int k = i;k < j; ++k) {
			++tp; int l = q[k].l, r = q[k].r;
			for(;val[r] != q[i].m;) --r;
			for(;val[l] != q[i].m;) ++l;
			L[tp] = id[l]; R[tp] = id[r];
		}
		Ans = mul(Ans, Dp(q[i].m));
	}
	for(int i = 1;i <= _n; ++i)
		if(!val[i])
			Ans = mul(Ans, Pow(A, i == _n ?: b[i + 1] - b[i]));
	return Ans;
}
int main() {
	for(int T = ri();T--;) 
		printf("%d\n", Work());
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值