LOJ #2331. 「清华集训 2017」某位歌姬的故事
题目叙述
给定长度为 \(n\) 的序列,和 \(m\) 个要求,每个要求的形式是区间 \([l,r]\) 内最大值为 \(t\) 。每个数要求\([1,k]\) 内的整数。求方案数。
\(n,k,l,r\le 10^9\) ,\(m\le 500\)。多组数据,数据组数 \(T\le 10\) 。
题解
这种和最大值最小值相关的问题一般来说可以考虑从小到大考虑每个区间。按照最大值的要求从小到大考虑每个区间。
假设目前考虑最大值为 \(v\) 的所有区间,如果前面所有区间的最大值 \(<v\) ,那么做法是简单的:
考虑前面的所有区间覆盖的位置,如果最大值为 \(v\) 覆盖的所有位置除去最大值 \(<v\) 的所有区间覆盖的位置,还有 \(t\) 个位置的话,那么方案数是 \(k^t-(k-1)^t\)。
如果最大值有相同的呢。容易发现我们只要把最大值都相同的情况处理掉就可以解决问题了。
做法类似于 NOI2020D1T2命运。设 \(f_{i,j}\) 表示前 \(i\) 个数,最后一个 \(=v\) 的数的位置在 \(j\) 的方案数,每次考虑以 \(i\) 为结尾的所有区间是否符合条件,不符合条件就在这个地方填 \(v\) ,否则随便填。
总结
- 关键是想到从小到大考虑所有区间,把问题拆分为最大值 \(=v\) 的所有区间这样的若干个问题方案数的乘积。
代码
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#define macro_expand(x) #x
#define print_macro(x) printf("%s\n",macro_expand(x))
#define FOR(i,l,r) for(int i=(l);i<=(r);++i)
#define ROF(i,r,l) for(int i=(r);i>=(l);--i)
using namespace std;
typedef long long LL;
bool chkmin(int &x,const int &y){return (x>y)?(x=y,1):0;}
bool chkmax(int &x,const int &y){return (x<y)?(x=y,1):0;}
const int MQ=1e3+5,Mod=998244353;
int ad(int x,int y){return ((x+y)>=Mod)?(x+y-Mod):(x+y);}
int dc(int x,int y){return ((x-y)<0)?(x-y+Mod):(x-y);}
int ml(int x,int y){return (LL)x*y%Mod;}
int add(int &x,int y){return ((x+=y)>=Mod)?(x-=Mod):x;}
int dec(int &x,int y){return ((x-=y)<0)?(x+=Mod):x;}
int ksm(int x,int y){
int ret=1;
for(;y;y>>=1,x=ml(x,x))if(y&1)ret=ml(ret,x);
return ret;
}
int N,Q,A;
int l[MQ],r[MQ],m[MQ],pt;
int li_m[MQ],totm,lr[MQ*2],totlr;
vector<int> seg[MQ];
int get(int *f,int len,int v){return lower_bound(f+1,f+len+1,v)-f;}
bool tag[MQ];
int count(int id){
// 注意要这么写
while(pt<id){
for(int i:seg[pt])for(int j=l[i];j<=r[i];++j)tag[j]=0;
++pt;
}
static int pos[MQ*2],len[MQ*2],cover[MQ*2],totp;
FOR(i,1,totlr)cover[i]=0;
for(int i:seg[id])cover[l[i]]++,cover[r[i]+1]--;
FOR(i,1,totlr)cover[i]+=cover[i-1];
totp=0;
FOR(i,1,totlr-1)if(tag[i]&&cover[i])pos[++totp]=i,len[totp]=lr[i+1]-lr[i];
// 特判totp为 0 的情况?需要吗?
if(totp==0)return 0;
static int le[MQ*2],ri[MQ*2],tot_le;
tot_le=0;
FOR(i,0,(int)seg[id].size()-1){
++tot_le;
le[tot_le]=lower_bound(pos+1,pos+totp+1,l[seg[id][i]])-pos;
ri[tot_le]=upper_bound(pos+1,pos+totp+1,r[seg[id][i]])-pos-1;
}
static int right_most[MQ*2];
FOR(i,1,totp)right_most[i]=0;
FOR(i,1,tot_le)chkmax(right_most[ri[i]],le[i]);
static int f[MQ*2][MQ*2];
// f[i][j]表示结尾在 i 之前的区间都合法,最后一个=id的点在 j 的方案数
FOR(i,1,totp)FOR(j,0,totp)f[i][j]=0;
// j 从 0 开始!!!
f[0][0]=1;
FOR(i,1,totp){
int tmp=ksm(dc(li_m[id],1),len[i]);
FOR(j,right_most[i],i-1)f[i][j]=ml(f[i-1][j],tmp);
int sum=0;
FOR(j,0,i-1)add(sum,f[i-1][j]);
add(f[i][i],ml(dc(ksm(li_m[id],len[i]),tmp),sum));
}
int ret=0;
FOR(i,right_most[totp],totp)add(ret,f[totp][i]);
return ret;
}
int main(){
// freopen("sing.in","r",stdin);
// freopen("sing.out","w",stdout);
int T=0;
scanf("%d",&T);
while(T--){
scanf("%d%d%d",&N,&Q,&A);
totlr=totm=0;
lr[++totlr]=1;
lr[++totlr]=N+1;
FOR(i,1,Q){
scanf("%d%d%d",&l[i],&r[i],&m[i]);
li_m[++totm]=m[i];
lr[++totlr]=l[i];
lr[++totlr]=r[i]+1;
}
sort(li_m+1,li_m+totm+1);
totm=unique(li_m+1,li_m+totm+1)-li_m-1;
sort(lr+1,lr+totlr+1);
totlr=unique(lr+1,lr+totlr+1)-lr-1;
FOR(i,1,totm)seg[i].clear();
FOR(i,1,Q){
l[i]=get(lr,totlr,l[i]);
r[i]=get(lr,totlr,r[i]+1)-1;
// 二分!
m[i]=get(li_m,totm,m[i]);
seg[m[i]].push_back(i);
}
FOR(i,1,totlr-1)tag[i]=1;
int ans=1;
pt=1;
FOR(i,1,totm){
int tmp=0;
ans=ml(ans,tmp=count(i));
}
static int sum[MQ*2];
memset(sum,0,sizeof(sum));
FOR(i,1,Q)sum[l[i]]+=1,sum[r[i]+1]-=1;
int pre=0;
FOR(i,1,totlr-1){
pre+=sum[i];
if(!pre)ans=ml(ans,ksm(A,lr[i+1]-lr[i]));
}
//特判没有东西覆盖情况
printf("%d\n",ans);
}
// fclose(stdin);
// fclose(stdout);
return 0;
}

浙公网安备 33010602011771号