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;
}

浙公网安备 33010602011771号