洛谷P4229 某位歌姬的故事 题解
题目链接:P4229 某位歌姬的故事
题意:
IA 是一名会唱歌的女孩子。
IOI2018 就要来了,IA 决定给参赛选手们写一首歌,以表达美好的祝愿。这首歌一共有 $n$ 个音符,第 $i$ 个音符的音高为 $h_i$。IA 的音域是 $A$,她只能唱出 $1\sim A$ 中的正整数音高。因此 $1\le h_i\le A$。
在写歌之前,IA 需要确定下这首歌的结构,于是她写下了 $Q$ 条限制,其中第 $i$ 条为:编号在 $l_i$ 到 $r_i$ 之间的音符的最高音高为 $m_i$。在确定了结构之后,她就可以开始写歌了。不过她还是想知道,一共有多少种可能的歌曲满足她的所有限制?她听说你还有 9 个月就要去 IOI 了,于是希望你帮她计算一下这个值。
输入格式:
输入的第一行包含一个整数 $T(T\le 20)$,代表测试数据的组数。
每组数据的第一行包含三个正整数 $n,Q,A$。接下来 $Q$ 行,每行三个整数 $l_i,r_i,m_i$,表示一条限制。保证 $1\le l_i\le r_i\le n, 1\le m_i\le A$。
输出格式:
输出只有一行,表示可能的歌曲数目。这个数可能很大,请将答案模 $998244353$ 输出。
数据范围:
$0\le n,A \le 9\times 10^{8},~ 0 \le Q \le 500$ 。
没完全懂,待研究。咕咕咕。。
首先可以把端点离散化一下,则以下可以认为 $n,Q$ 同阶。
设 $\mathtt{mx[i]}$ 表示第 $i$ 个位置的元素可以达到的最大值。
这可以通过对覆盖当前位置的每个限制暴力取 $\min$ 得到。
对于一个限制 $(l_i,r_i,m_i)$ ,显然只有区间 $[l_i,r_i]$ 中,$\mathtt{mx[x]}=m_i$ 的位置 $x$ 可能使得限制满足。
因为当 $\mathtt{mx[x]}<m_i$ 时,说明这个位置被一个更紧的限制卡住了;
当 $\mathtt{mx[x]} > m_i$ 时,说明区间 $i$ 根本没有覆盖到 $x$ 。这说明 $\mathtt{mx[]}$ 取值不同的位置互不影响。
考虑枚举 $\mathtt{mx[x]}$ 的值 $k$ ,将 $\mathtt{mx[x]}=k$ 的下标 $x$ 拿出来dp。
则限制转化为: $[l_i,r_i]$ 中存在一个元素顶到 $k$ 的上界。
设 $f_{i,j}$ 表示填了前 $i$ 个位置,最后一个顶到 $k$ 的位置是 $j$ 时的方案数。
转移时枚举一个新的位置 $i$ ,预先维护出所有右端点为 $i$ 的区间中左端点的最大值进行转移即可。
时间复杂度 $\mathcal{O}(TQ^2)$
代码:
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
typedef pair<int,int> pii;
const int mod = 998244353;
void up(int &x,int y) { x < y ? x = y : 0; }
void down(int &x,int y) { x > y ? x = y : 0; }
void mul(int &x,int y) { x = x * y % mod; }
void add(int &x,int y) { x = (x + y % mod) % mod; }
namespace FastIO
{
#define gc() readchar()
#define pc(a) putchar(a)
#define SIZ (int)(1e6+15)
char buf1[SIZ],*p1,*p2;
char readchar()
{
if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);
return p1==p2?EOF:*p1++;
}
template<typename T>void read(T &k)
{
char ch=gc();T x=0,f=1;
while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}
while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}
k=x*f;
}
template<typename A, typename ...B>
void read(A &x, B &...y) { return read(x), read(y...); }
template<typename T>void write(T k)
{
if(k<0){k=-k;pc('-');}
static T stk[66];T top=0;
do{stk[top++]=k%10,k/=10;}while(k);
while(top){pc(stk[--top]+'0');}
}
}using namespace FastIO;
#define Fi first
#define Se second
#define N ((int)(1e3+15))
int n,Q,A,ans=1,len;
int L[N],R[N],m[N],p[N];
int f[N][N],mx[N],lim[N],idx[N];
vector<int> vec; vector<pii> opt;
int qpow(int a,int b)
{
int ans = 1, base = a % mod;
for(; b; b >>= 1)
{
if(b & 1) ans = ans * base % mod;
base = base * base % mod;
}return ans;
}
void clear()
{
memset(mx,0x3f,sizeof(mx));
vec.clear(); opt.clear(); ans = 1;
}
int exist(int m,int s)
{ return (qpow(m,s) - qpow(m-1, s) + mod) % mod; }
int siz(int x) { return vec[x] - vec[x-1]; }
int getid(int x)
{ return lower_bound(vec.begin(), vec.end(), x) - vec.begin() + 1; }
int calc(int m,int l,int r)
{
int res = 0, o = 0; f[0][0] = 1;
for(int i=1; i<=len; i++) if(mx[i] == m) idx[++o] = i;
for(int i = l, _l, _r; i<=r; i++)
{
_l = *lower_bound(idx + 1, idx + 1 + o, L[p[i]]);
_r = *(lower_bound(idx + 1, idx + 1 + o, R[p[i]]) - 1);
up(lim[_r], _l);
}
for(int i=1; i<=len; i++) up(lim[i], lim[i-1]);
for(int i=1; i<=o; i++)
for(int j=0; j<idx[i]; j++)
{
add(f[i][idx[i]], f[i-1][j] * exist(m, siz(idx[i])) % mod);
if(lim[idx[i]] <= j) f[i][j] = f[i-1][j] * qpow(m-1, siz(idx[i])) % mod;
}
for(int i=1; i<=idx[o]; i++) add(res, f[o][i]);
for(int i=0; i<=len; i++) lim[i] = 0;
for(int i=1; i<=o; i++)
for(int j=0; j<=idx[i]; j++) f[i][j] = 0;
return res;
}
void work()
{
clear(); read(n,Q,A);
for(int i=1; i<=Q; i++)
{
read(L[i], R[i], m[i]); ++R[i];
opt.push_back({L[i], 1}); opt.push_back({R[i], -1});
vec.push_back(L[i]); vec.push_back(R[i]); p[i] = i;
}
sort(vec.begin(), vec.end());
vec.erase(unique(vec.begin(), vec.end()), vec.end()); len = vec.size();
for(int i=1; i<=Q; i++)
{
L[i] = getid(L[i]); R[i] = getid(R[i]);
for(int j = L[i]; j < R[i]; j++) down(mx[j], m[i]);
}
sort(p + 1, p + 1 + Q, [](int x,int y) { return m[x] < m[y]; });
for(int l = 1, r; l<=Q; l = r + 1)
{
for(r = l; r < Q && m[p[r+1]] == m[p[l]]; ++r);
mul(ans, calc(m[p[l]], l, r));
}
sort(opt.begin(), opt.end()); int cur = 0, pre = 1;
for(int l = 0, r; l < opt.size(); l = r + 1)
{
if(!cur) mul(ans, qpow(A, opt[l].Fi - pre));
r = l; cur += opt[l].Se;
while(r+1 < opt.size() && opt[r+1].Fi == opt[l].Fi) cur += opt[++r].Se;
pre = opt[l].Fi;
}
mul(ans, qpow(A, n - pre + 1)); write(ans); pc('\n');
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
// freopen("check.in","r",stdin);
// freopen("check.out","w",stdout);
int _Q; read(_Q); while(_Q--) work();
return 0;
}
参考文献: