loj 6077 「2017 山東一輪集訓 Day7」逆序對 題解
阿新 • • 發佈:2020-12-29
loj 6077 「2017 山東一輪集訓 Day7」逆序對
一個經典問題
我們一個一個加入元素,第i個貢獻的逆序對數量在區間\([0,i-1]\)內
問題也就是有多少個排列\(x\)滿足:
\[\sum _{i}x_i=k\ |\ x_i\in [0,i-1] \]可以考慮容斥:
如果有j個不滿足條件,也就是\(x_i\geq i\)
我們可以將不滿足條件的\(x_i\)都減去\(i\)。
然後剩下的插板法分配。
對於一對\(i,j\)我們要知道有多少個排列\(p\)滿足:
\[\begin{align*} & (1). |p|=i\\ & (2). \sum p_i=j\\ & (3). \forall k\in[2,i],p_{k}>p_{k-1}\\ & (4). \forall k,p_{k}\in [1,n] \end {align*} \]我們可以設\(dp_{i,j}\)表示滿足條件\((i,j)\)的方案數。
有兩種轉移:
- 整體+1,\(dp_{i,j}\leftarrow dp_{i,j-i}\)
- 整體+1,然後在開頭新增一個1:\(dp_{i,j}\leftarrow dp_{i-1,j-i}\)
這樣我們就可以滿足前三個限制了,第四個限制怎麼辦呢?
如果我們在恰好有一個元素\(>n\)的時候就將他減去,就會方便很多了。
則還有一個轉移:
- \(dp_{i,j}\leftarrow -dp_{i-1,j-(n+1)}\)
可以發現\(i\leq \sqrt{n}\),所以時間複雜度為\(O(\sqrt n\times k)\)
/* { ###################### # Author # # Gary # # 2020 # ###################### */ #include <bits/stdc++.h> #define rb(a,b,c) for(int a=b;a<=c;++a) #define rl(a,b,c) for(int a=b;a>=c;--a) #define LL long long #define IT iterator #define PB push_back #define II(a,b) make_pair(a,b) #define FIR first #define SEC second #define FREO freopen("check.out","w",stdout) #define rep(a,b) for(int a=0;a<b;++a) #define SRAND mt19937 rng(chrono::steady_clock::now().time_since_epoch().count()) #define random(a) rng()%a #define ALL(a) a.begin(),a.end() #define POB pop_back #define ff fflush(stdout) #define fastio ios::sync_with_stdio(false) #define check_min(a,b) a=min(a,b) #define check_max(a,b) a=max(a,b) using namespace std; //inline int read(){ // int x=0; // char ch=getchar(); // while(ch<'0'||ch>'9'){ // ch=getchar(); // } // while(ch>='0'&&ch<='9'){ // x=(x<<1)+(x<<3)+(ch^48); // ch=getchar(); // } // return x; //} const int INF = 0x3f3f3f3f; typedef pair<int, int> mp; /*} */ const int MOD = 1e9 + 7; int n, k; const int SQRT = 450; const int MAXN = 1e5 + 20; int dp[SQRT + 1][MAXN + 1]; //取了i個,和為j int fact[MAXN + MAXN]; LL quick(LL A, LL B) { if (B == 0) return 1; LL tmp = quick(A, B >> 1); tmp *= tmp; tmp %= MOD; if (B & 1) tmp *= A, tmp %= MOD; return tmp; } int inv(int x) { return quick(x, MOD - 2); } void add(int &A, int B) { A += B; if (A >= MOD) A -= MOD; } int c(int A, int B) { return 1ll * fact[A] * inv(fact[B]) % MOD * inv(fact[A - B]) % MOD; } int main() { scanf("%d%d", &n, &k); dp[0][0] = 1; rb(i, 1, SQRT) { rb(j, i, k) { dp[i][j] = (dp[i][j - i] + dp[i - 1][j - i]) % MOD; if (j > n) { add(dp[i][j], MOD - dp[i - 1][j - (n + 1)]); } } } fact[0] = 1; rb(i, 1, 200000) { fact[i] = 1ll * fact[i - 1] * i % MOD; } int rest = 0; rb(i, 0, k) { int tmp = c(k - i + n - 1, n - 1); rb(j, 0, SQRT) { int have = 1ll * tmp * dp[j][i] % MOD; if (j & 1) { have = MOD - have; } add(rest, have); } } cout << rest << endl; return 0; }