LOJ #3157. 「NOI2019」机器人

题目叙述

求每个位置都在 \([A_i,B_i]\) 范围内的满足下面条件的序列个个数:

  • 对于任意位置 \(i\) ,向左延长满足都比 \(A_i\) 小的长度比向右延长不比 \(A_i\) 大的长度绝对值相差不超过 2 。

题解

从最大值出发考虑。相当于建立笛卡尔树类似状物了。
\(f_{l,r,x}\) 表示区间 \([l,r]\)\(\le x\) 的方案数。
转移直接枚举区间 \([l,r]\) 的中点 \(m\) ,从 \(f_{l,m-1,x}\)\(f_{m+1,r,x-1}\) 转移。
可以大概估计出,有效区间数量是 \(n\log n\) 级别的。
值域很大,可以发现对于 \(x\) 来说 \(f_{l,r,x}\) 是一个分段的多项式函数。
多项式的次数不超过 \(r-l+1\)
于是考虑记录 \(n+1\) 个点值然后使用拉格朗日插值得到每个多项式是什么。
多项式的分段点的两个端点必然是每个位置限制的端点。
对于每个区间,分别 dp 出来前 \(n+1\) 项的点值,然后根据多项式的结论计算出区间最后一个数的点值,把这个数作为下一阶段 dp 的初始值。
然后再继续 dp 就可以了。

总结

  • 对于这种大小相关的东西要思考笛卡尔树。相当于从最大值最小值思考了。
  • 值域构成区间的分段动态规划考虑拉格朗日插值。如果值域比较大的话。简单来讲,值域比较大就应该拉格朗日插值,猜测多项式相关的结论。

代码

#include <cstdio>
#include <iostream>
#include <algorithm>
#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##ADJK=(r);i<=i##ADJK;++i)
#define ROF(i,r,l) for(int i=(r),i##ADJK=(l);i>=i##ADJK;--i)
using namespace std;
typedef long long LL;
const int MN=305,Mod=1e9+7;
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,A[MN],B[MN],c[MN*2],totc,len,id[MN][MN];
int totid;
void get_id(int l,int r){
	if(l>r||id[l][r])return;
	id[l][r]=++totid;
	if(l==r)return;
	if((r-l+1)&1){
		int mid=(l+r)>>1;
		get_id(l,mid-1),get_id(mid+1,r);
		get_id(l,mid-2),get_id(mid,r);
		get_id(l,mid),get_id(mid+2,r);
	}else{
		int mid=(l+r)>>1;
		get_id(l,mid-1),get_id(mid+1,r);
		get_id(l,mid),get_id(mid+2,r);
	}
}
int f[3005][MN];
bool vis[3005];
int now_val;
void calc(int l,int r){
	if(vis[id[l][r]])return;
	vis[id[l][r]]=1;
	FOR(i,1,len)f[id[l][r]][i]=0;
	if(l>r)f[id[l][r]][0]=1;
	else{
		int pos[]={0,0,0,0},top=0; // static 的使用……
		top=0;
		int mid=(l+r)>>1;
		if(r-l+1==1)top=1,pos[1]=mid;
		else if((r-l+1)&1)top=3,pos[1]=mid,pos[2]=mid-1,pos[3]=mid+1;
		else top=2,pos[1]=mid,pos[2]=mid+1;
		FOR(i,1,top){
			int p=pos[i];
			if(A[p]<=now_val&&now_val<B[p]){
				calc(l,p-1),calc(p+1,r);
				FOR(j,1,len)
					add(f[id[l][r]][j],ml(f[id[l][p-1]][j],f[id[p+1][r]][j-1]));
			}
		}
	}
	FOR(j,1,len)add(f[id[l][r]][j],f[id[l][r]][j-1]);
}
int caf[MN],fac[MN];
void lagrange(int l,int r){
	if(l+N>=r){FOR(i,1,totid)f[i][0]=f[i][r-l+1];return;}
	int pro=1,x=r-l+1;
	static int inv[MN];
	FOR(i,1,N+1)pro=ml(pro,x-i),inv[i]=ksm(x-i,Mod-2);
	FOR(j,1,totid)f[j][0]=0;
	FOR(j,1,N+1){
		int post=ml(pro,ml(inv[j],ml(caf[j-1],((N+1-j)&1)?(Mod-caf[N+1-j]):caf[N+1-j])));
		FOR(i,1,totid)add(f[i][0],ml(f[i][j],post));
	}
}
void init(int L){
	fac[0]=1;
	FOR(i,1,L)fac[i]=ml(fac[i-1],i);
	caf[L]=ksm(fac[L],Mod-2);
	ROF(i,L-1,0)caf[i]=ml(caf[i+1],i+1);
}
int main(){
	freopen("robot.in","r",stdin);
	freopen("robot.out","w",stdout);
	scanf("%d",&N);
	init(N+1);
	fac[0]=1;
	FOR(i,1,N+1)fac[i]=ml(fac[i-1],i);
	caf[N+1]=ksm(fac[N+1],Mod-2);
	FOR(i,1,N)scanf("%d%d",&A[i],&B[i]),c[++totc]=A[i],c[++totc]=++B[i];
	sort(c+1,c+totc+1);
	totc=unique(c+1,c+totc+1)-c-1;
	FOR(i,1,N){
		A[i]=lower_bound(c+1,c+totc+1,A[i])-c;
		B[i]=lower_bound(c+1,c+totc+1,B[i])-c;
	}
	get_id(1,N);
	FOR(i,1,totc-1){
		len=min(c[i+1]-c[i],N+1);
		now_val=i;
		FOR(j,0,totid)vis[j]=0;
		FOR(l,1,N)FOR(r,l,N)if(id[l][r])calc(l,r); // 不能直接calc(1,N)
		lagrange(c[i],c[i+1]-1);
	}
	printf("%d\n",f[id[1][N]][0]);
	fclose(stdin);
	fclose(stdout);
	return 0;
}
posted @ 2022-07-27 10:41  YouthRhythm  阅读(69)  评论(0)    收藏  举报