[UOJ#346][清华集训2017]某位歌姬的故事(DP)

博客详细解析了洛谷P4229及UOJ#346问题的解决方案,涉及动态规划(DP)策略。当限制条件的区间端点x不同时,可以离散化并按x从小到大处理,计算每个x的贡献。若x相同,则再次离散化,通过DP状态f[i][j]表示前i段内处理情况,最终复杂度为O(m^2)。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Address

Solution

  • orz 讲课现场切掉此题的神仙 Lagoon
  • 可以发现,如果一个限制为 max ⁡ i = l r h i = x \max_{i=l}^rh_i=x maxi=lrhi=x ,另一个限制为 max ⁡ i = a b h i = y \max_{i=a}^bh_i=y maxi=abhi=y y &lt; x y&lt;x y<x
  • 那么对于任意的 k ∈ [ l , r ] ⋂ [ a , b ] k\in[l,r]\bigcap[a,b] k[l,r][a,b] h k h_k hk max ⁡ i = l r h i \max_{i=l}^rh_i maxi=lrhi 没有影响
  • 所以如果每个限制的 x x x 互不相同,我们可以把区间端点离散化之后,按照 x x x 从小到大做
  • 具体地,如果一个限制为 max ⁡ i = l r h i = x \max_{i=l}^rh_i=x maxi=lrhi=x [ l , r ] [l,r] [l,r] 内有 k k k 个位置没有 x x x 严格更小的限制覆盖
  • 那么答案乘上 x k − ( x − 1 ) k x^k-(x-1)^k xk(x1)k
  • 最后乘上不被任何限制区间覆盖的位置的贡献
  • O ( m 2 ) O(m^2) O(m2)
  • 而如果 x x x 有相同的,那么把 x x x 相同的放在一起做
  • 还是按 x x x 从小往大做,设满足 max ⁡ i = l r h i = x \max_{i=l}^rh_i=x maxi=lrhi=x 的限制有 s s s
  • 那么对这 s s s 个区间端点再离散化一次,为了保证接下去 DP 的复杂度
  • 注意到区间端点离散化之后,整个序列被分割为 O ( s ) O(s) O(s) 段,且每一段一定完全包含或完全不包含于任意一个满足 max ⁡ i = l r h i = x \max_{i=l}^rh_i=x maxi=lrhi=x 的限制区间 [ l , r ] [l,r] [l,r]
  • c n t i cnt_i cnti 表示第 i i i 段中有多少个位置没被 x x x 严格更小的限制覆盖
  • DP 状态: f [ i ] [ j ] f[i][j] f[i][j] 表示处理到前 i i i 段,上一个恰好填 x x x 的位置在第 j j j 段,且所有右端点在前 i i i 段内的限制区间都至少包含一个恰好填 x x x 的位置的方案数(被覆盖的位置不能填数,其他位置只能填 [ 1 , x ] [1,x] [1,x] 内的数)
  • f [ 0 ] [ 0 ] = 1 f[0][0]=1 f[0][0]=1
  • f [ i + 1 ] [ i + 1 ] + = f [ i ] [ j ] × ( x c n t i + 1 − ( x − 1 ) c n t i + 1 ) f[i+1][i+1]+=f[i][j]\times(x^{cnt_{i+1}}-(x-1)^{cnt_{i+1}}) f[i+1][i+1]+=f[i][j]×(xcnti+1(x1)cnti+1)
  • f [ i + 1 ] [ j ] + = f [ i ] [ j ] × ( x − 1 ) c n t i + 1 f[i+1][j]+=f[i][j]\times(x-1)^{cnt_{i+1}} f[i+1][j]+=f[i][j]×(x1)cnti+1
  • 注意如果以第 i + 1 i+1 i+1 段为结尾的最短区间左端点落到了第 j j j 段之后,那么第三种转移不能进行
  • O ( s 2 ) O(s^2) O(s2)
  • 最后的复杂度 O ( m 2 ) O(m^2) O(m2)

Code

#include <bits/stdc++.h>

template <class T>
inline void read(T &res)
{
	res = 0; bool bo = 0; char c;
	while (((c = getchar()) < '0' || c > '9') && c != '-');
	if (c == '-') bo = 1; else res = c - 48;
	while ((c = getchar()) >= '0' && c <= '9')
		res = (res << 3) + (res << 1) + (c - 48);
	if (bo) res = ~res + 1;
}

template <class T>
inline T Max(const T &a, const T &b) {return a > b ? a : b;}

const int N = 505, M = 1010, ZZQ = 998244353;

int qpow(int a, int b)
{
	int res = 1;
	while (b)
	{
		if (b & 1) res = 1ll * res * a % ZZQ;
		a = 1ll * a * a % ZZQ;
		b >>= 1;
	}
	return res;
}

int n, q, A, tot, real[M], Tot, Real[M], f[M][M], s[M], mxr[M], ans;
bool vis[M], siv[M];

struct node
{
	int l, r, m;
} a[N];

inline bool comp(node a, node b)
{
	return a.m < b.m;
}

void solve(int le, int ri, int num)
{
	Real[1] = 1; Real[Tot = 2] = tot;
	for (int i = le; i <= ri; i++)
		Real[++Tot] = a[i].l, Real[++Tot] = a[i].r + 1;
	std::sort(Real + 1, Real + Tot + 1);
	Tot = std::unique(Real + 1, Real + Tot + 1) - Real - 1;
	for (int i = 1; i < tot; i++) siv[i] = 0;
	for (int i = le; i <= ri; i++)
		for (int j = a[i].l; j <= a[i].r; j++)
			siv[j] = 1;
	for (int i = 1; i < tot; i++)
		s[i] = s[i - 1] + (vis[i] || !siv[i] ? 0 : real[i + 1] - real[i]),
			vis[i] = vis[i] || siv[i];
	f[0][0] = 1;
	for (int i = 1; i < Tot; i++) mxr[i] = 0;
	for (int i = le; i <= ri; i++)
	{
		int r = std::lower_bound(Real + 1, Real + Tot + 1, a[i].r + 1) - Real - 1,
			l = std::lower_bound(Real + 1, Real + Tot + 1, a[i].l) - Real;
		mxr[r] = Max(mxr[r], l);
	}
	for (int i = 1; i < Tot; i++)
	{
		int c0 = qpow(num - 1, s[Real[i + 1] - 1] - s[Real[i] - 1]),
			c1 = (qpow(num, s[Real[i + 1] - 1] - s[Real[i] - 1]) - c0 + ZZQ) % ZZQ;
		for (int j = 0; j <= i; j++) f[i][j] = 0;
		for (int j = 0; j < i; j++)
			f[i][j] = (1ll * c0 * f[i - 1][j] + f[i][j]) % ZZQ,
			f[i][i] = (1ll * c1 * f[i - 1][j] + f[i][i]) % ZZQ;
		for (int j = 0; j < mxr[i]; j++) f[i][j] = 0;
	}
	int res = 0;
	for (int i = mxr[Tot - 1]; i < Tot; i++)
		res = (res + f[Tot - 1][i]) % ZZQ;
	ans = 1ll * ans * res % ZZQ;
}

void work()
{
	ans = 1;
	memset(vis, 0, sizeof(vis));
	read(n); read(q); read(A);
	for (int i = 1; i <= q; i++)
		read(a[i].l), read(a[i].r), read(a[i].m);
	std::sort(a + 1, a + q + 1, comp);
	real[1] = 1; real[tot = 2] = n + 1;
	for (int i = 1; i <= q; i++)
		real[++tot] = a[i].l, real[++tot] = a[i].r + 1;
	std::sort(real + 1, real + tot + 1);
	tot = std::unique(real + 1, real + tot + 1) - real - 1;
	for (int i = 1; i <= q;)
	{
		int nxt = i;
		while (nxt <= q && a[i].m == a[nxt].m)
		{
			a[nxt].l = std::lower_bound(real + 1, real + tot + 1, a[nxt].l) - real;
			a[nxt].r = std::lower_bound(real + 1, real + tot + 1,
				a[nxt].r + 1) - real - 1;
			nxt++;
		}
		solve(i, nxt - 1, a[i].m);
		i = nxt;
	}
	int res = 0;
	for (int i = 1; i < tot; i++) if (!vis[i]) res += real[i + 1] - real[i];
	printf("%d\n", 1ll * ans * qpow(A, res) % ZZQ);
}

int main()
{
	int T; read(T);
	while (T--) work();
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值