[NOI2018]冒泡排序
[NOI2018]冒泡排序?
題解
性質(zhì)+模型轉(zhuǎn)化
?
首先,一個(gè)排列是“好”的,當(dāng)且僅當(dāng):每個(gè)數(shù),要么是前綴最大值,要么是后綴最小值。(討論i和Pi的關(guān)系即可證明)
也就是,排列不能存在>=3的下降子序列!
換句話(huà)說(shuō),假設(shè)之前填了i個(gè)數(shù),最大值是mx,那么第i+1個(gè)數(shù),要么是剩下數(shù)的最小值,要么是比mx大的數(shù)。
字典序,肯定按位考慮,轉(zhuǎn)化成沒(méi)有限制的情況,
所以先處理沒(méi)有限制的情況
這樣DP,
$f[i][j]$剩下i個(gè)數(shù),比之前最大值大的數(shù)有j個(gè)的方案數(shù)。
第n-i+1個(gè)位置,要么填最小值,要么填這j個(gè)數(shù)之一。
填這j個(gè)中第k大的數(shù)(它就成為了新的最大值),就只能剩下j-k個(gè)比最大值大的了。
轉(zhuǎn)移:$f[i][j]=\sum_{k=0}^{j}f[i-1][k]$,k=0代表填了最小值。前綴和優(yōu)化
當(dāng)然,i>=j必須保證
?
然后可以卡位。
之前比最大值大的數(shù)有nw個(gè),第i個(gè)位置數(shù)是ai,
第i個(gè)位置:
1.$p_i>a_i$
$p_i$一定是一個(gè)比最大值大的數(shù)
如果$a_i$是前綴最大值,則nw=n-a[i]
否則,nw=n-前綴最大值
顯然為了嚴(yán)格大于,如果nw=0,一定不行。
?
自由之后,方案數(shù)是:$\sum_{j=0}^{nw-1}f[n-i][j]=f[n-i+1][nw-1]$
2.$p_i=a_i$
判斷$a_i$是不是前綴最大值或者后綴最小值即可
?
O(T*n^2)
過(guò)不去。
瓶頸在于O(n^2)DP
這個(gè)DP很模式化啊,,,
$f[i][j]=\sum_{k=0}^{j}f[i-1][k]$
能不能發(fā)現(xiàn)組合意義?
結(jié)論:
$f[i][j]=C(i+j-1,j)-C(i+j-1,j-2)$
證明:
顯然必須有i>=j
本質(zhì)是,(0,0)往(i,j)走,每次要么往右走,要么往右上走,不越過(guò)直線(xiàn)(i=j)的方案數(shù)。
可以對(duì)偶成:從(1,1)走,每次往右往上走,不能越過(guò)直線(xiàn)(i=j-1)的方案數(shù)
藍(lán)色折線(xiàn)和綠色折線(xiàn)一一對(duì)應(yīng)!
不合法的也一一對(duì)應(yīng)!(請(qǐng)自行畫(huà)圖)
從(1,1)走,每次往右往上走,不能越過(guò)直線(xiàn)(i=j-1)的方案數(shù)
這個(gè)直接卡特蘭數(shù)一樣,對(duì)稱(chēng)容斥下即可。
?
O(n)
?
// luogu-judger-enable-o2 #include<bits/stdc++.h> #define reg register int #define il inline #define fi first #define se second #define mk(a,b) make_pair(a,b) #define numb (ch^'0') #define pb push_back #define solid const auto & #define enter cout<<endl #define pii pair<int,int> using namespace std; typedef long long ll; template<class T>il void rd(T &x){char ch;x=0;bool fl=false;while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);for(x=numb;isdigit(ch=getchar());x=x*10+numb);(fl==true)&&(x=-x);} template<class T>il void output(T x){if(x/10)output(x/10);putchar(x%10+'0');} template<class T>il void ot(T x){if(x<0) putchar('-'),x=-x;output(x);putchar(' ');} template<class T>il void prt(T a[],int st,int nd){for(reg i=st;i<=nd;++i) ot(a[i]);putchar('\n');} namespace Modulo{ const int mod=998244353; il int ad(int x,int y){return x+y>=mod?x+y-mod:x+y;} il int sub(int x,int y){return ad(x,mod-y);} il int mul(int x,int y){return (ll)x*y%mod;} il void inc(int &x,int y){x=ad(x,y);} il void inc2(int &x,int y){x=mul(x,y);} il int qm(int x,int y=mod-2){int ret=1;while(y){if(y&1) ret=mul(x,ret);x=mul(x,x);y>>=1;}return ret;} template<class ...Args>il int ad(const int a,const int b,const Args &...args) {return ad(ad(a,b),args...);} template<class ...Args>il int mul(const int a,const int b,const Args &...args) {return mul(mul(a,b),args...);} } using namespace Modulo; namespace Miracle{ const int N=1200000+5; int n; int jie[N],inv[N]; int C(int n,int m){if(n<0||m<0||n<m) return 0;return mul(jie[n],inv[m],inv[n-m]); } int F(int n,int m){if(m==0) return 1;if(m==1) return n;return sub(C(n+m-1,m),C(n+m-1,m-2)); } int a[N]; bool is[N]; int main(){int t;rd(t);n=N-3;jie[0]=1;for(reg i=1;i<=n;++i) jie[i]=mul(jie[i-1],i);inv[n]=qm(jie[n]);for(reg i=n-1;i>=0;--i) inv[i]=mul(inv[i+1],i+1);while(t--){rd(n);for(reg i=1;i<=n;++i) rd(a[i]);int mi=n+3;for(reg i=n;i>=1;--i){is[i]=0;if(a[i]<mi){mi=a[i];is[i]=1;}}int ans=0;int nw=n,mx=0;for(reg i=1;i<=n;++i){nw=n-mx;int lp=nw;if(a[i]>mx) lp=n-a[i]; if(lp){ans=ad(ans,F(n-i+1,lp-1));}if(a[i]>mx){mx=a[i];}else if(!is[i]){break;}}printf("%d\n",ans);}return 0; }} signed main(){Miracle::main();return 0; }/*Author: *Miracle* */?
發(fā)現(xiàn)本質(zhì):一個(gè)數(shù)要么是前綴最大值,要么是后綴最小值
無(wú)限制?可以DP,f[i][j]記錄剩下的數(shù)以及比最大值大的數(shù)
字典序?按位考慮。變成無(wú)限制。
O(n^2)?觀察DP式子找到組合意義!
?
?
?
?
轉(zhuǎn)載于:https://www.cnblogs.com/Miracevin/p/11032215.html
總結(jié)
以上是生活随笔為你收集整理的[NOI2018]冒泡排序的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Shell编程—【05】Linux的fi
- 下一篇: 时间戳与日期的互换