「NOI2019」机器人 拉格朗日插值
「NOI2019」机器人 拉格朗日插值
Part 20
爆搜,单调栈检查方案即可。
期望得分 20
Part 35-50
考虑一个性质。
对于一个序列 \(H\)。我们找到其中高度最大的那一个(如果有多个最大的取位置最右边的)。
设这个位置为 \(pos\)。容易发现 \(pos\) 左边的不能到 \(pos\) 右边,而 \(pos\) 右边的不能到 \(pos\) 左边。
而且 \([1,pos-1],[pos+1,n]\) 这两段区间长度的绝对值之差不能大于 \(2\)。
我们发现 \(pos\) 相当于就是将一段序列切割出来了。
那么我们可以考虑区间 dp。设 \(f_{i,j}(x)\) 表示 \([i,j]\) 这段区间中高度最大为 \(x\) 且满足题目所给条件的方案数。
转移:
枚举满足条件的 \(pos\)。用前缀和能优化到 \(O(1)\) 转移。
期望得分:35
发现 \(pos\) 的位置只有几个,所以所有的状态数不会很多,由于 \(pos\) 在中点左右的位置,猜想只有 \(O(n\log n)\) 个区间数是有用的。爆搜一下发现 \(n=300\) 时有 \(3000\) 不到的有用区间数,差不多是 \(10n\) 的样子。那么给每个区间标号后最多有 \(3000\times B\) 个状态需要转移。
期望得分:50
Part 60
注意到 \(f_{i,j}(x)\) 的初值。
可以把它看做一个分段的多项式。\(1\) 即 \(x^0\)。
那么我们发现 \(f_{i,j}(x)\) 就是若干个多项式之积,不难推知其度为 \(j-i\)。
考虑 \(A_i=1,B_i=10^9\) 的部分分。
那么 \(f_{i,i}(x)\) 的初值一定为 \(1\)。
也就是在该约束下我们的多项式只有一段。
而我们要求 \(\sum f_{1,n}(x)\)。
我们知道任意一个度为 \(n\) 的多项式的前缀和是一个度为 \(n+1\) 的多项式。
所以我们直接上拉格朗日插值。即可求得。
(前 60 分的代码在正解下面也放了)
Part 100
根据 Part 60 的部分我们已经知道 \(f_{i,j}(x)\) 是一个多项式了。
注意到 \(n\) 一共只有 \(300\)。也就是说这个分段多项式最多只有 \(2n\) 段的样子。
我们将区间设成左闭右开方便处理。
假设我们求出了 \([1,C_{i})\) 的 \(f_{i,j}(x)\) 的前缀和,考虑求得 \(\sum f_{i,j}(x),x\in[C_i,C_{i+1}]\) 的值。
如果区间长度小于等于 \(n+1\)。因为我们有了之前的前缀和,直接暴力 dp 出\(f_{i,j}(x),x\in[C_i,C_{i+1})\) 即可。
如果区间大于 \(n+1\) 。我们考虑暴力 dp 出 \(f_{i,j}(x),x\in[C_i,C_i+n]\) 。然后根据这 \(n\) 个点我们插值求得 \(\sum f_{i,j}(x),(x\in [C_i,C_i+n])\) 的多项式(其前缀和的多项式),然后计算出 \(\sum f_{i,j}(C_{i+1}-1)\) 即可。
然后继续处理下一段。由于 \(x\) 值我们可以选连续的,所以可以做到 \(O(n)\) 插值。
那么总复杂度是 \(O(10\times n^3)\)。
常数较大。考虑优化常数,我们发现通过插值求 \(f_{i,j}(x)\) 的时候,只需要插 \(i-j+2\) 个点就行,不需要插 \(n+1\) 个点。取模也同样可以优化。以及可以预处理的都预处理一下,比如逆元啥的,不然可能快速幂求逆元会成为瓶颈。
最后代码如下:(插值的时候怕错多插了几个点)
#include<bits/stdc++.h>
#define ll long long
#define Mod(x) ((x>=MOD)&&(x-=MOD))
using namespace std;
const int MOD = 1e9+7;
const int MAXN = 305;
int A[MAXN],B[MAXN],n;
int tot,id[305][305],m,siz,MX,T,L[2500],R[2500];
ll f[2500][305],C[MAXN<<1],fac[MAXN],df[MAXN],inv[MAXN],pre[MAXN];
bool vis[MAXN][MAXN];
ll qpw(ll x,ll b)
{
ll r=1;
for(;b;b>>=1,x=x*x%MOD) if(b&1) r=r*x%MOD;
return r;
}
vector < pair<int,int> > vec;
map< pair<int,int> ,bool >mp;
void init(int l,int r)
{
if(id[l][r]) return ;
if(l>r)
{
if(!mp.count(make_pair(l,r)))vec.push_back(make_pair(l,r));
mp[make_pair(l,r)]=1;
return ;
}
id[l][r]=++tot;
for(int i=l;i<=r;++i) if(abs(2*i-l-r)<=2)
init(l,i-1),init(i+1,r);
}
void dfs(int l,int r)
{
if(vis[l][r]) return ;
vis[l][r]=1;int cur=id[l][r];
if(l>r) f[cur][0]=1;
else
{
for(int i=max(l,(l+r)/2-1);i<=min(r,(l+r)/2+1);++i)
{
if(abs(2*i-l-r)<=2&&A[i]<=T&&T<B[i])
{
dfs(l,i-1);dfs(i+1,r);
int u=id[l][i-1],v=id[i+1][r];
for(int k=1;k<=MX;++k)
{
f[cur][k]+=f[u][k]*f[v][k-1]%MOD;
Mod(f[cur][k]);
}
}
}
}
for(int i=1;i<=MX;++i)
f[cur][i]+=f[cur][i-1],Mod(f[cur][i]);
}
void Solve(int l,int r)
{
if(r-l+1<=n+1)
{
for(int i=1;i<=tot;++i) f[i][0]=f[i][r-l+1];
return ;
}
ll P=1;
for(int i=1;i<=n+1;++i)
P=P*(r-l-i+1+MOD)%MOD;
pre[n+2]=1;
for(int i=n+1;i>=1;--i)
{
inv[i]=qpw(r-l-i+1+MOD,MOD-2)%MOD;
pre[i]=pre[i+1]*inv[i]%MOD;
}
for(int k=1;k<=tot;++k)
{
ll res=0;
int len=R[k]-L[k]+1;
for(int i=1;i<=len+1;++i)
{
ll p=P*inv[i]%MOD*pre[len+2]%MOD,q=df[len+1-i]*fac[i-1]%MOD;
res+=f[k][i]*p%MOD*q%MOD;
Mod(res);
}
f[k][0]=res;
}
}
int main()
{
freopen("robot.in","r",stdin);
freopen("robot.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;++i) scanf("%d %d",&A[i],&B[i]);
for(int i=1;i<=n;++i)
{
B[i]++;
C[i]=A[i];
C[i+n]=B[i];
}
sort(C+1,C+1+2*n);
int siz=unique(C+1,C+1+2*n)-C-1;
for(int i=1;i<=n;++i)
{
A[i]=lower_bound(C+1,C+1+siz,A[i])-C;
B[i]=lower_bound(C+1,C+1+siz,B[i])-C;
}
fac[0]=df[0]=1;
for(int i=1;i<=n+1;++i) fac[i]=fac[i-1]*i%MOD,df[i]=df[i-1]*(MOD-i)%MOD;
fac[n+1]=qpw(fac[n+1],MOD-2);df[n+1]=qpw(df[n+1],MOD-2);
for(int i=n;i>=0;--i) fac[i]=fac[i+1]*(i+1)%MOD,df[i]=df[i+1]*(MOD-i-1)%MOD;
init(1,n);
int Node=tot;
for(pair<int,int> v:vec) id[v.first][v.second]=++Node;
for(int i=1;i<=n;++i)
{
for(int j=1;j<=n;++j)
{
if(id[i][j])
{
L[id[i][j]]=i;
R[id[i][j]]=j;
}
}
}
for(int i=1;i<siz;++i)
{
T=i;
MX=min(n+1ll,C[i+1]-C[i]);memset(vis,0,sizeof vis);
for(int j=1;j<=n;++j) for(int k=j;k<=n;++k) if(id[j][k]) dfs(j,k);
Solve(C[i],C[i+1]-1);
for(int j=1;j<=Node;++j)for(int k=1;k<=MX;++k) f[j][k]=0;
}
printf("%lld\n",f[id[1][n]][0]);
return 0;
}
前 60 分
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MOD = 1e9+7;
const int MAXN = 305;
int A[MAXN],B[MAXN],n;
namespace pt20
{
int C[MAXN],Ans,st[MAXN],L[MAXN],R[MAXN],top;
bool check()
{
for(int i=1;i<=n;++i) L[i]=0,R[i]=n+1;
top=0;
for(int i=1;i<=n;++i)
{
while(top&&C[i]>=C[st[top]]) --top;
if(top) L[i]=st[top];
st[++top]=i;
}
top=0;
for(int i=n;i>=1;--i)
{
while(top&&C[i]>C[st[top]]) --top;
if(top) R[i]=st[top];
st[++top]=i;
}
for(int i=1;i<=n;++i)
if(abs((R[i]-i)-(i-L[i]))>2) return false;
return true;
}
void dfs(int p)
{
if(p>n)
{
if(check()) ++Ans;
return ;
}
for(int i=A[p];i<=B[p];++i)
{
C[p]=i;
dfs(p+1);
}
}
void Solve()
{
Ans=0;
dfs(1);
printf("%d\n",Ans);
}
}
namespace pt50
{
int tot,id[305][305],m;
ll f[3005][10005];
void dfs(int l,int r)
{
if(id[l][r]) return ;
int cur=++tot;id[l][r]=cur;
if(l>r) f[cur][0]=1;
else
{
for(int i=l;i<=r;++i)
{
if(abs(2*i-l-r)<=2)
{
dfs(l,i-1);dfs(i+1,r);
for(int k=A[i];k<=B[i];++k)
{
f[cur][k]+=(f[id[l][i-1]][k]*f[id[i+1][r]][k-1])%MOD;
f[cur][k]%=MOD;
}
}
}
}
for(int i=1;i<=10000;++i)
f[cur][i]=(f[cur][i-1]+f[cur][i])%MOD;
}
void Solve(int mx)
{
m=mx;
dfs(1,n);
printf("%lld\n",f[id[1][n]][m]);
}
ll Get_ans(int mx)
{
tot=0;memset(f,0,sizeof f);memset(id,0,sizeof id);m=mx;
for(int i=1;i<=n;++i) B[i]=mx;
dfs(1,n);
return f[id[1][n]][m];
}
}
namespace pt60
{
ll qpw(ll x,ll b)
{
ll r=1;
for(;b;b>>=1,x=x*x%MOD) if(b&1) r=r*x%MOD;
return r;
}
void Solve()
{
ll Ans=0,tmp=0,X=1e9;
for(int i=1;i<=100;++i)
{
tmp=pt50::Get_ans(i)%MOD;
ll val=1;
for(int j=1;j<=100;++j)
if(i!=j) val=val*(X-j+MOD)%MOD*qpw(i-j+MOD,MOD-2)%MOD;
Ans=(Ans+val*tmp%MOD)%MOD;
}
printf("%lld\n",Ans);
}
}
int main()
{
freopen("robot.in","r",stdin);
freopen("robot.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;++i) scanf("%d %d",&A[i],&B[i]);
int mx=0;
for(int i=1;i<=n;++i) mx=max(mx,B[i]);
if(n<=7) pt20::Solve();
else if(mx<=10000) pt50::Solve(mx);
else pt60::Solve();
return 0;
}