首先我們發現乙個數不能既被往左換又被往右換。也就是說不能有任何乙個數左邊有比他大的,又被有比他小的。
也就是最長下降子串行長度不超過 2 。
所以我們一定可以找到 2 個上公升序列包含所有的數。
於是容易想到 $o(n^2)$ 的 dp:
$dp_$ 表示加入了 $i$ 個數,最大值為 $j$ 的情況下,填完的方案數。
那麼,如果下乙個數小於 $i$ ,那肯定是填小於 $j$ 的第乙個,否則就是填乙個比 $j$ 更大的值。
我們把這個東西看做括號序列,填乙個比 $j$ 大的值就相當於加入若干個左括號,然後再加入乙個右括號;填乙個比 $j$ 小的數字就相當於加入乙個右括號。
這個東西的方案數可以用類似於卡特蘭數的公式 $c_i = \binom - \binom $ 的推導方法來得到。
這個請自行搜尋,懶得畫圖了。
#pragma gcc optimize("ofast","inline")#include #define clr(x) memset(x,0,sizeof (x))
#define for(i,a,b) for (int i=a;i<=b;i++)
#define fod(i,b,a) for (int i=b;i>=a;i--)
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define _seed_ ('c'+'l'+'y'+'a'+'k'+'i'+'o'+'i')
#define outval(x) printf(#x" = %d\n",x)
#define outvec(x) printf("vec "#x" = ");for (auto _v : x)printf("%d ",_v);puts("")
#define outtag(x) puts("----------"#x"----------")
#define outarr(a,l,r) printf(#a"[%d...%d] = ",l,r);\
for(_v2,l,r)printf("%d ",a[_v2]);puts("");
using namespace std;
typedef long long ll;
ll read()
const int n=1200005,mod=998244353;
void add(int &x,int y)
void del(int &x,int y)
int pow(int x,int y)
int n;
int fac[n],inv[n];
void prework()
int c(int n,int m)
int p[n];
int q[n],head,tail;
int pos,vis[n];
int calc(int x,int y)
void main()
vis[p[i]]=1;
} cout<}int main()
NOI2018 氣泡排序
noi2018 氣泡排序 題解性質 模型轉化 首先,乙個排列是 好 的,當且僅當 每個數,要麼是字首最大值,要麼是字尾最小值。討論i和pi的關係即可證明 也就是,排列不能存在 3的下降子串行!換句話說,假設之前填了i個數,最大值是mx,那麼第i 1個數,要麼是剩下數的最小值,要麼是比mx大的數。字典...
NOI2018 氣泡排序
題面 吐槽 好像發的pdf題面的冒泡是掛掛的,還好我還會氣泡排序 逃 具體思路 首先發現當這個序列的最長下降序列長度大於2時,一定不符合要求 那麼我們可以想出乙個 o n 2 的dp f i,j 表示前 i 個數,設其中最大的數為 mx 後面的數中有 j 個數比 mx 小的方案數 然後發現 f i,...
BZOJ 5416 Noi2018 氣泡排序
bzoj 5416 noi2018 氣泡排序 dp 組合數 樹狀陣列 好題。合法的排列的交換次數剛好是交換次數的下界,也就是說不能有多餘的交換。也就是對於ai這個數,只能從i到ai這乙個方向走。考慮x,y,z三個數 x y z y需要和x z各交換一次,這顯然不能使y這個數滿足只向乙個方向移動這個條...