P4229-某位歌姬的故事【dp】

12 篇文章 0 订阅

正题

题目链接:https://www.luogu.com.cn/problem/P4229


题目大意

求有多少个长度为 n n n的序列 a a a,满足 i ∈ [ 1 , n ] , a i ∈ [ 1 , A ] orall iin[1,n],a_iin[1,A] i∈[1,n],ai∈[1,A],还有 Q Q Q个限制形如
max { a j } ( j ∈ [ l i , r i ] ) = m i max{a_j}(jin[l_i,r_i])=m_i max{aj}(j∈[li,ri])=mi

1 ≤ n , A ≤ 9 × 1 0 8 , 1 ≤ m i ≤ A , 1 ≤ Q ≤ 500 , 1 ≤ T ≤ 20 1leq n,Aleq 9 imes 10^8,1leq m_ileq A,1leq Qleq 500,1leq Tleq 20 1≤n,A≤9×108,1≤mi≤A,1≤Q≤500,1≤T≤20


解题思路

首先我们第一步肯定是把每个区间的端点提出来离散化,这样我们的区间数就是 O ( Q ) O(Q) O(Q)级别的了。

然后考虑到对于两个有交的区间 [ l 1 , r 1 ] [l_1,r_1] [l1,r1]限制为 m 1 m_1 m1, [ l 2 , r 2 ] [l_2,r_2] [l2,r2]限制为 m 2 m_2 m2,在 m 1 < m 2 m_1<m_2 m1<m2时这两个区间交的那一部分显然不会对第二个区间产生影响,因为这个区间肯定合法并且不能是最大值。

那么我们考虑求出每个区间能够到达的最大值 l i m i lim_i limi,然后对一个所有的限制 [ l , r , w ] [l,r,w] [l,r,w],我们都只需要考虑 l i m i = w lim_i=w limi=w的区间。

现在相当于对于每个单独的小区间我们可以选择上到最大值或者没有最大值。然后要求是每个区间至少有一个最大值。

考虑 d p dp dp,设 f i , j f_{i,j} fi,j表示现在做到第 i i i个区间,上一个顶到最大值的区间是 j j j时的方案,因为我们只处理 l i m i = w lim_i=w limi=w的区间,所以一个区间最多被做一次。

时间复杂度: O ( T Q 2 ) O(TQ^2) O(TQ2)


code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=2100,P=998244353;
struct node{
	ll l,r,w;
}q[N];
ll T,n,m,A,tot,cnt,b[N];
ll wc[N],bc[N],lim[N],len[N];
ll f[N][N],rim[N],loc[N],pos[N];
ll power(ll x,ll b){
	ll ans=1;
	while(b){
		if(b&1)ans=ans*x%P;
		x=x*x%P;b>>=1;
	}
	return ans;
}
bool cmp(node x,node y){
	if(x.w!=y.w)return x.w<y.w;
	if(x.l!=y.l)return x.l<y.l;
	return x.r<y.r;
}
ll calc(ll w,ll L,ll R){
	tot=0;
	for(ll i=1;i<=cnt;i++){
		if(lim[i]==w)
			loc[++tot]=i,rim[tot]=0;
		pos[i]=tot;
	}
	for(ll i=L;i<=R;i++){
		q[i].r=pos[q[i].r];
		if(loc[pos[q[i].l]]!=q[i].l)
			q[i].l=pos[q[i].l]+1;
		else q[i].l=pos[q[i].l];
		rim[q[i].r]=max(rim[q[i].r],q[i].l);
	}
	ll r=0;f[0][0]=1;
	for(ll i=1;i<=tot;i++){
		for(ll j=0;j<=i;j++)f[i][j]=0;
		for(ll j=rim[i];j<i;j++)
			(f[i][j]+=f[i-1][j]*wc[loc[i]]%P)%=P;
		for(ll j=0;j<i;j++)
			(f[i][i]+=f[i-1][j]*bc[loc[i]]%P)%=P;
	}
	ll ans=0;
	for(ll j=0;j<=tot;j++)
		(ans+=f[tot][j])%=P;
	return ans;
}
void solve(){
	scanf("%lld%lld%lld",&n,&m,&A);
	for(ll i=1;i<=m;i++){
		scanf("%lld%lld%lld",&q[i].l,&q[i].r,&q[i].w);
		q[i].r++;b[++cnt]=q[i].l;b[++cnt]=q[i].r;
	}
	b[++cnt]=1;b[++cnt]=n+1;
	sort(b+1,b+1+cnt);
	sort(q+1,q+1+m,cmp);
	cnt=unique(b+1,b+1+cnt)-b-1;
	for(ll i=1;i<=cnt;i++)lim[i]=A+1;
	for(ll i=1;i<=m;i++){
		q[i].l=lower_bound(b+1,b+1+cnt,q[i].l)-b;
		q[i].r=lower_bound(b+1,b+1+cnt,q[i].r)-b-1;
		bool flag=false;
		for(ll j=q[i].l;j<=q[i].r;j++){
			if(lim[j]>=q[i].w)flag=true;
			lim[j]=min(lim[j],q[i].w);
		}
		if(!flag){puts("0");return;}
	}
	ll ans=1;
	for(ll i=1;i<cnt;i++){
		len[i]=b[i+1]-b[i];
		if(lim[i]==A+1)ans=ans*power(A,len[i])%P;
		wc[i]=power(lim[i]-1,len[i]);
		bc[i]=(power(lim[i],len[i])-wc[i]+P)%P;
	}
	ll L,R=0;cnt--;
	while(R<m){
		L=R+1;R=L;
		while(R<m&&q[R+1].w==q[L].w)R++;
		ans=ans*calc(q[L].w,L,R)%P;
	}
	printf("%lld
",ans);
	return;
}
signed main()
{
	scanf("%lld",&T);
	while(T--){
		cnt=0;solve();
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值