[LOJ#2331]「清华集训 2017」某位歌姬的故事

[LOJ#2331]「清华集训 2017」某位歌姬的故事

试题描述

IA是一名会唱歌的女孩子。

IOI2018就要来了,IA决定给参赛选手们写一首歌,以表达美好的祝愿。这首歌一共有 (n) 个音符,第 (i) 个音符的音高为 (h_i)。IA的音域是 (A),她只能唱出 (1sim A) 中的正整数音高。因此 (1le h_ile A)

在写歌之前,IA需要确定下这首歌的结构,于是她写下了 (Q) 条限制,其中第 (i) 条为:编号在 (l_i)(r_i) 之间的音符的最高音高为 (m_i)。在确定了结构之后,她就可以开始写歌了。不过她还是想知道,一共有多少种可能的歌曲满足她的所有限制?她听说你还有 (9) 个月就要去IOI了,于是希望你帮她计算一下这个值。

输入

从标准输入读入数据。

输入的第一行包含一个整数 (T(Tle 20)),代表测试数据的组数。

每组数据的第一行包含三个正整数 (n,Q,A)。接下来 (Q) 行,每行三个整数 (l_i,r_i,m_i),表示一条限制。保证 (1le l_ile r_ile n)(1le m_ile A)

输出

输出到标准输出。

输出文件只有一行,表示可能的歌曲数目。这个数可能很大,请将答案模 (998244353) 输出。

输入示例1

1
3 2 3
1 2 3
2 3 2

输出示例1

3

输入示例2

2
4 2 4
1 2 3
2 3 4
7 3 74
3 6 56
2 5 56
3 7 70

输出示例2

20
160326468

数据规模及约定

对于 (100\%) 的数据,(n le 9 imes 10^8)(Q le 500)(A le 9 imes 10^8)(m le A)

题解

首先套路肯定要将所有区间按照音高限制从小到大排序,然后将每个位置的最高音高处理出来,然后对于每一个不同的音高限制拿出所有对应的位置和限制区间做一个 dp,不同音高之间的方案数用乘法连接。

至于那个 dp,方法有很多,我的方法是设 (f(i, j)) 表示满足了前 (i) 个区间,处理到了位置 (j),且 (j) 上放的是最大值,(j)不能再出现最大值。转移本来是 (O(n)) 的,可以用前缀和优化至 (O(1))(确切地说是 (O(mathrm{log}n)) 因为有个快速幂)。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std;
#define rep(i, s, t) for(int i = (s); i <= (t); i++)
#define dwn(i, s, t) for(int i = (s); i >= (t); i--)

int read() {
	int x = 0, f = 1; char c = getchar();
	while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
	while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
	return x * f;
}

#define maxm 510
#define maxn 2010
#define MOD 998244353
#define LL long long

struct Seg {
	int l, r, h;
	Seg() {}
	Seg(int _1, int _2, int _3): l(_1), r(_2), h(_3) {}
	bool operator < (const Seg& t) const { return h != t.h ? h < t.h : r < t.r; }
} qs[maxm];
int n, cntl, getl[maxn], cntn, num[maxn], len[maxn], lim[maxn], pos[maxn], val[maxn];

int Pow(int a, int b) {
	int ans = 1, t = a;
	while(b) {
		if(b & 1) ans = (LL)ans * t % MOD;
		t = (LL)t * t % MOD; b >>= 1;
	}
	return ans;
}
int Inv(int a) { return Pow(a, MOD - 2); }

int cq, cn, f[2][maxn], sf[2][maxn], le[maxn], sl[maxn], inv[maxn];
Seg s[maxm];
void dp(int& mul, int h) {
	if(h == 1) return ;
	
	memset(f, 0, sizeof(f));
	memset(sf, 0, sizeof(sf));
	int cur = 0;
	f[0][0] = sf[0][0] = 1; inv[0] = 1;
	rep(i, 1, cn) sf[cur][i] = sf[cur][i-1], sl[i] = sl[i-1] + le[i], inv[i] = Inv(Pow(h - 1, sl[i]));
	s[0].r = 0;
	rep(i, 1, cq) {
		cur ^= 1;
		rep(j, 0, s[i].l - 1) f[cur][j] = sf[cur][j] = 0;
		rep(j, s[i].l, s[i].r) {
			f[cur][j] = f[cur^1][j];
			if(j > s[i-1].r) {
				f[cur][j] += (LL)Pow(h - 1, sl[s[i-1].r]) * Pow(h, sl[j-1] - sl[s[i-1].r]) % MOD * sf[cur^1][s[i-1].r] % MOD * (Pow(h, le[j]) - Pow(h - 1, le[j]) + MOD) % MOD;
				if(f[cur][j] >= MOD) f[cur][j] -= MOD;
			}
			sf[cur][j] = (sf[cur][j-1] + (LL)f[cur][j] * inv[j] % MOD) % MOD;
		}
		rep(j, s[i].r + 1, cn) sf[cur][j] = sf[cur][j-1], f[cur][j] = 0;
	}
	
	mul = (LL)mul * sf[cur][cn] % MOD * Pow(h - 1, sl[cn]) % MOD;
	return ;
}

void work() {
	n = read(); int q = read(), m = read();
	cntl = cntn = 0;
	rep(i, 1, q) {
		int l = read(), r = read(), h = read();
		qs[i] = Seg(l, r, h);
		getl[++cntl] = l; getl[++cntl] = r; val[i] = h;
	}
	sort(qs + 1, qs + q + 1);
	getl[++cntl] = 1; getl[++cntl] = n;
	sort(getl + 1, getl + cntl + 1);
	cntl = unique(getl + 1, getl + cntl + 1) - getl - 1;
	rep(i, 1, cntl) {
		num[++cntn] = getl[i]; len[cntn] = 1;
		if(i < cntl && getl[i] + 1 < getl[i+1]) num[++cntn] = getl[i] + 1, len[cntn] = getl[i+1] - getl[i] - 1;
	}
	memset(lim, 0, sizeof(lim));
	rep(i, 1, q) {
		int l = qs[i].l = lower_bound(num + 1, num + cntn + 1, qs[i].l) - num;
		int r = qs[i].r = lower_bound(num + 1, num + cntn + 1, qs[i].r) - num;
		int mx = 0; bool has = 0;
		rep(j, l, r)
			if(!lim[j]) lim[j] = qs[i].h, has = 1;
			else mx = max(mx, lim[j]);
		if(!has && mx < qs[i].h) return (void)puts("0");
	}
	
	sort(val + 1, val + q + 1);
	int ans = 1, i = 1, tq = unique(val + 1, val + q + 1) - val - 1;
	rep(v, 1, tq) {
		cq = 0;
		while(i <= q && qs[i].h == val[v]) s[++cq] = qs[i++];
		cn = 0;
		rep(j, 1, cntn) if(lim[j] == val[v]) le[pos[j] = ++cn] = len[j];
		rep(j, 1, cq) {
			while(lim[s[j].l] != val[v]) s[j].l++;
			while(lim[s[j].r] != val[v]) s[j].r--;
			s[j].l = pos[s[j].l], s[j].r = pos[s[j].r];
		}
		dp(ans, val[v]);
	}
	rep(i, 1, cntn) if(!lim[i]) ans = (LL)ans * Pow(m, len[i]) % MOD;
	printf("%d
", ans);
	return ;
}

int main() {
	int T = read();
	while(T--) work();
	
	return 0;
}
原文地址:https://www.cnblogs.com/xiao-ju-ruo-xjr/p/8058794.html