NOI2019 兩道插值可還行.
一個數不可能向右移動到超過後綴最大值的位置,也不可能向前移到前綴最大值之前的位置.
那麼就考慮基於最大值的分治(DP)
令 $f[l][r][x]$ 表示當前區間爲 $[l,r]$ 最大值爲 $x$ 的方案數.
然後轉移的話枚舉 $k$ 爲最大值出現的位置(有多個的話則是最後出現的位置).
那麼就將問題分成兩個子問題了,時間複雜度爲 $O(nMW)$,其中 $M,W$ 分別爲區間個數和值域.
$35$ pts 的暴力分就是 $M=n^2$,即每個區間都枚舉,總複雜度是 $O(n^2W)$.
但是我們打表發現有用的區間 $M$ 最多爲 $3000$,那麼提前記憶化搜索的話複雜度就是 $O(10nW)$ 了.
考慮 $l=1,r=10^9$ 的點,由於每個位置的取值範圍都是相同的,我們可以暴力求出最大值爲 $1$ ~ $n$ 的點.
然後可以用容斥+組合來算.
還有一種能推廣到正解的做法就是用拉格朗日插值法.
可以歸納,$f[l][r]$ 是一個不超過 $r-l$ 次的多項式.
考慮當 $l=r$ 時顯然成立(就是一個常數),然後對 $f$ 求前綴和的話多項式的次數+1.
最後在合併的時候本質上是兩個多項式相乘,次數爲 $len-1$.
對於每個位置取值不同的點就將所有點排序,然後以相鄰兩個點爲值域仿照上面的做法去做 DP.
那麼假設當前的區間爲 $[a,b]$ 那麼就將 $[1,a)$ 的部分當作常數項處理.
這裏有兩個細節要注意:
1. 加入區間的時候要加入 $[a,b)$ 因爲如果加入 $[a,a]$ 的話可能會處理不到 $[a,a]$ 這種情況.
2. 處理到 $[a_{i-1},a_{i}]$ 的時候要先處理 $[a_{i-1},a_{i}-1]$ 然後再處理 $[a_{i},a_{i}]$,因爲端點處可能存在交界.
code:
#include <cstdio> #include <ctime> #include <vector> #include <cmath> #include <cstring> #include <algorithm> #define N 307 #define M 3005 #define ll long long #define mod 1000000007 #define setIO(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout) using namespace std; int qpow(int x,int y) { int tmp=1; for(;y;y>>=1,x=(ll)x*x%mod) { if(y&1) tmp=(ll)tmp*x%mod; } return tmp; } inline int get_inv(int x) { return qpow(x,mod-2); } inline int ADD(int x,int y) { return x+y>=mod?x+y-mod:x+y; } inline int DEC(int x,int y) { return x-y<0?x-y+mod:x-y; } namespace Lagrange { int x[N],y[N],fac[N],inv[N],pre[N],suf[N]; void prep() { fac[0]=1; for(int i=1;i<N;++i) fac[i]=(ll)fac[i-1]*i%mod; inv[1]=1; for(int i=2;i<N;++i) { inv[i]=(ll)(mod-mod/i)*inv[mod%i]%mod; } inv[0]=1; for(int i=1;i<N;++i) { inv[i]=(ll)inv[i-1]*inv[i]%mod; } } void init(int v,int kth) { for(int i=0;i<=kth;++i) x[i]=i; pre[0]=suf[kth+1]=1; for(int i=1;i<=kth;++i) { pre[i]=(ll)(v-x[i-1]+mod)*pre[i-1]%mod; } for(int i=kth;i>=1;--i) { suf[i]=(ll)(v-x[i]+mod)*suf[i+1]%mod; } } int solve(int v,int kth) { int an=0; for(int i=0;i<=kth;++i) { int up=1,dn=1; dn=(ll)inv[i]*inv[kth-i]%mod; if((kth-i)&1) dn=(ll)dn*(mod-1)%mod; up=(ll)pre[i]*suf[i+1]%mod; an=ADD(an,(ll)y[i]*up%mod*dn%mod); } return an; } }; int n; bool vis[M]; int a[N],b[N],e[M],lim,pr; int id[N][N],dp[M][N],len[M],tot,cnt; struct data { int l,r; data(int l=0,int r=0):l(l),r(r){} }arr[M]; void dfs(int l,int r) { if(l>r||id[l][r]) return; id[l][r]=++cnt; arr[cnt]=data(l,r); len[cnt]=r-l+1; if(l==r) return; int mid=(l+r)>>1,len=r-l+1; if(len&1) { dfs(l,mid-2),dfs(l,mid-1),dfs(l,mid); dfs(mid+2,r),dfs(mid+1,r),dfs(mid,r); } else { dfs(l,mid-1),dfs(l,mid); dfs(mid+2,r),dfs(mid+1,r); } } void get(int l,int r,int x,int now); void solve(int l,int r) { int now=id[l][r]; if(vis[now]||l>r) return; vis[now]=1; for(int i=1;i<=lim;++i) { dp[now][i]=0; } if(l==r) { for(int i=1;i<=lim;++i) { dp[now][i]=dp[now][i-1]+(i+pr>=a[l]&&i+pr<=b[l]); if(dp[now][i]>=mod) dp[now][i]-=mod; } return; } int mid=(l+r)>>1,len=(r-l+1); if(len&1) { get(l,r,mid-1,now); get(l,r,mid,now); get(l,r,mid+1,now); } else { get(l,r,mid,now); get(l,r,mid+1,now); } for(int i=1;i<=lim;++i) { dp[now][i]=ADD(dp[now][i],dp[now][i-1]); } } void get(int l,int r,int x,int now) { if(x<l||x>r) return; solve(l,x-1),solve(x+1,r); int u=id[l][x-1],v=id[x+1][r]; for(int i=1;i<=lim;++i) { if(i+pr>=a[x]&&i+pr<=b[x]) dp[now][i]=ADD(dp[now][i],(ll)dp[u][i]*dp[v][i-1]%mod); } } int main() { // setIO("robot"); scanf("%d",&n); for(int i=1;i<=n;++i) { scanf("%d%d",&a[i],&b[i]); e[++tot]=a[i],e[++tot]=b[i]+1; } Lagrange::prep(); sort(e+1,e+1+tot); dfs(1,n); int x,y,z; for(int i=0;i<=n+3;++i) { dp[0][i]=1; } for(int i=1;i<=tot;++i) { if(e[i]!=e[i-1]) { pr=e[i-1]; lim=min(e[i]-e[i-1]-1,n+3); memset(vis,false,sizeof(vis)); solve(1,n); if(lim==e[i]-e[i-1]-1) { for(int j=1;j<=cnt;++j) dp[j][0]=dp[j][lim]; } else { for(int j=1;j<=cnt;++j) { for(int p=0;p<=len[j];++p) { Lagrange::y[p]=dp[j][p]; } Lagrange::init(e[i]-e[i-1]-1,len[j]); dp[j][0]=Lagrange::solve(e[i]-e[i-1]-1,len[j]); } } lim=1,pr=e[i]-1; memset(vis,false,sizeof(vis)); solve(1,n); for(int j=1;j<=cnt;++j) { dp[j][0]=dp[j][1]; } } } printf("%d\n",dp[id[1][n]][0]); return 0; }