【NOI2019】 机器人 【区间dp】【插值】【下降幂多项式】【分段函数】
題意:有個長度為nnn的序列aaa,ai∈[Li,Ri]a_i\in [L_i,R_i]ai?∈[Li?,Ri?]。從一個位置sss可以往左直到≥as\geq a_s≥as?,往右直到>as> a_s>as?。求有多少種可能的序列滿足從任意位置向左或向右的最大步數的差的絕對值不超過222。
n≤300,Ri≤109n\leq 300,R_i\leq 10^9n≤300,Ri?≤109
National Olympics in Interpolation
先對于一個給定序列判斷是否合法
考慮最右邊的最大值,它可以到序列的任意位置,所以它一定在序列正中間的O(1)O(1)O(1)個位置。
左邊的點都到不了右邊,右邊的點都到不了左邊,所以可以分開考慮。
現在考慮計數。設dp(l,r,x)dp(l,r,x)dp(l,r,x)表示[l,r][l,r][l,r]這個區間最大值恰好為xxx且滿足題目中的要求的方案數
dp(l,r,x)=∑∣l+r?2k∣≤2[∑i≤xdp(l,k?1,i)][∑i<xdp(k+1,r,i)]dp(l,r,x)=\sum _{|l+r-2k|\leq2}[\sum _{i\leq x}dp(l,k-1,i)][\sum_{i<x}dp(k+1,r,i)]dp(l,r,x)=∣l+r?2k∣≤2∑?[i≤x∑?dp(l,k?1,i)][i<x∑?dp(k+1,r,i)]
根據套路,大膽猜想:
dp(l,r,x)是關于x的r?l次多項式\text{dp(l,r,x)是關于$x$的$r-l$次多項式}dp(l,r,x)是關于x的r?l次多項式
證明很套路,略
先不管值域的限制,我們現在需要維護多項式求點值前綴和和多項式乘法
拉格朗日是個不錯的方法,但是被涼心出題人卡了
這里需要用下降冪多項式的黑科技
首先有個顯而易見的式子:
xi ̄?(x?1)i ̄=i(x?1)i?1 ̄x^{\underline i}-(x-1)^{\underline i}=i(x-1)^{\underline {i-1}}xi??(x?1)i?=i(x?1)i?1?
有什么用呢?
考慮一個下降冪多項式
f(x)=∑iaixi ̄f(x)=\sum_{i}a_ix^{\underline i}f(x)=i∑?ai?xi?
我們想用一些辦法快速求它的前綴和。因為一些奇怪的原因,這里我們只加到x?1x-1x?1,即不包括這個位置本身
g(x)=∑i=1x?1f(i)=∑i=1x?1∑jajij ̄g(x)=\sum_{i=1}^{x-1}f(i)\\=\sum_{i=1}^{x-1}\sum_{j}a_ji^{\underline j}g(x)=i=1∑x?1?f(i)=i=1∑x?1?j∑?aj?ij?
=∑jaj∑i=1x?1ij ̄=\sum_j a_j\sum_{i=1}^{x-1} i^{\underline j}=j∑?aj?i=1∑x?1?ij?
對于一個已知的iii,考慮這個怎么求:
∑x=1nxi ̄\sum_{x=1}^nx^{\underline i}x=1∑n?xi?
用上面的式子裂下項
∑x=1n(x+1)i+1 ̄?xi+1 ̄i+1\sum_{x=1}^n\frac{(x+1)^{\underline{i+1}}-x^{\underline {i+1}}}{i+1}x=1∑n?i+1(x+1)i+1??xi+1??
=(n+1)i+1 ̄i+1=\frac{(n+1)^{^{\underline {i+1}}}}{i+1}=i+1(n+1)i+1??
所以
g(x)=∑iaixi+1 ̄i+1g(x)=\sum_i a_i\frac{x^{\underline {i+1}}}{i+1}g(x)=i∑?ai?i+1xi+1??
形式上和連續多項式的積分一樣,不知道是不是巧合
再加上原來的多項式就得到了前綴和
對于兩個下降冪多項式f(x),g(x)f(x),g(x)f(x),g(x)的乘法,可以從低到高枚舉f(x)f(x)f(x)的每一項,對于一個xi ̄x^{\underline i}xi?,與右邊的一個(x?i)j ̄(x-i)^{\underline j}(x?i)j?相乘可以得到xi+j ̄x^{\underline {i+j}}xi+j?。每枚舉一個iii就再用上面的式子O(n)O(n)O(n)地把g(x)g(x)g(x)變成g(x?1)g(x-1)g(x?1),然后暴力算卷積即可。
帶上Li,RiL_i,R_iLi?,Ri?的話就把[1,Li?1][1,L_i-1][1,Li??1]和[Ri+1,+∞][R_i+1,+\infin][Ri?+1,+∞]的部分置為000,直接大力維護分段函數即可
復雜度是什么?可以吃嗎?
#include <iostream> #include <cstdio> #include <cstring> #include <cctype> #include <vector> #include <utility> #define MAXN 350 using namespace std; const int MOD=1e9+7; typedef long long ll; inline int qpow(int a,int p) {int ans=1;while (p){if (p&1) ans=(ll)ans*a%MOD;a=(ll)a*a%MOD,p>>=1;}return ans; } int inv[MAXN]; inline int add(const int& x,const int& y){return x+y>=MOD? x+y-MOD:x+y;} inline int dec(const int& x,const int& y){return x<y? x-y+MOD:x-y;} typedef vector<int> poly; typedef pair<poly,int> seg; typedef vector<seg> func; #define fir first #define sec second #define mp make_pair inline int f(const poly& a,const int& x) {int ans=0;for (int i=0,m=1;i<(int)a.size();m=(ll)m*(x-i)%MOD,++i)ans=(ans+(ll)a[i]*m)%MOD;return ans; } inline poly operator +(poly a,const poly& b) {a.resize(max(a.size(),b.size()));for (int i=0;i<(int)b.size();i++) a[i]=add(a[i],b[i]);return a; } inline poly operator *(const poly& a,poly b) {poly c(a.size()+b.size()-1);for (int i=0;i<(int)a.size();i++){for (int j=0;j<(int)b.size();j++) c[i+j]=(c[i+j]+(ll)a[i]*b[j])%MOD;for (int j=1;j<(int)b.size();j++) b[j-1]=(b[j-1]+(ll)b[j]*j)%MOD;}return c; } inline poly integ(poly a) {a.push_back(0);for (int i=a.size()-1;i>0;i--) a[i]=(ll)a[i-1]*inv[i]%MOD;a[0]=0;return a; } inline func operator +(func a,func b) {func c;int i=0,j=0,pos=0;while (true){c.push_back(mp(a[i].fir+b[j].fir,pos));if (i==(int)a.size()-1&&j==(int)b.size()-1) break;if (i<(int)a.size()-1&&(j==(int)b.size()-1||a[i+1].sec<b[j+1].sec)) pos=a[++i].sec;else pos=b[++j].sec;if (i<(int)a.size()-1&&a[i+1].sec<=pos) ++i;if (j<(int)b.size()-1&&b[j+1].sec<=pos) ++j;}return c; } inline func operator *(func a,func b) {func c;int i=0,j=0,pos=0;while (true){c.push_back(mp(a[i].fir*b[j].fir,pos));if (i==(int)a.size()-1&&j==(int)b.size()-1) break;if (i<(int)a.size()-1&&(j==(int)b.size()-1||a[i+1].sec<b[j+1].sec)) pos=a[++i].sec;else pos=b[++j].sec;if (i<(int)a.size()-1&&a[i+1].sec<=pos) ++i;if (j<(int)b.size()-1&&b[j+1].sec<=pos) ++j;}return c; } inline func integ(func a) {for (int i=0;i<(int)a.size();i++){a[i].fir=integ(a[i].fir);if (i) a[i].fir[0]=dec(f(a[i-1].fir,a[i].sec),f(a[i].fir,a[i].sec));}return a; } inline func cut(const func& a,const int& l,const int& r) {func b;b.push_back(mp(poly(1),0));for (int i=0;i<(int)a.size();i++)if (a[i].sec<=r&&(i==(int)a.size()-1||l<a[i+1].sec))b.push_back(mp(a[i].fir,max(a[i].sec,l)));b.push_back(mp(poly(1),r+1));return b; } func dp[MAXN][MAXN]; int L[MAXN],R[MAXN]; void dfs(int l,int r) {if (dp[l][r].size()) return;dp[l][r].push_back(mp(poly(1),0));if (l>r) return (void)(dp[l][r][0].fir[0]=1);for (int k=l;k<=r;k++)if (-2<=l+r-2*k&&l+r-2*k<=2){dfs(l,k-1),dfs(k+1,r);func a=dp[l][k-1],b=dp[k+1][r];if (l<k) a=a+integ(a);if (k<r) b=integ(b);dp[l][r]=dp[l][r]+cut(a*b,L[k],R[k]); } } int main() {inv[1]=1;for (int i=2;i<MAXN;i++) inv[i]=(ll)inv[MOD%i]*(MOD-MOD/i)%MOD;int n;scanf("%d",&n);for (int i=1;i<=n;i++) scanf("%d%d",&L[i],&R[i]);dfs(1,n); printf("%d\n",integ(dp[1][n]).back().fir[0]);return 0; } 創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的【NOI2019】 机器人 【区间dp】【插值】【下降幂多项式】【分段函数】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 公司网站怎么推广(公司网站怎么推广赚钱)
- 下一篇: 【UOJ549】序列妙妙值【异或】【根号