LOJ - 6077 逆序對
阿新 • • 發佈:2021-08-09
今天是星期一啊!
目錄
\(\text{100 pts}\)
個數 互異,可以這樣轉移:
大概是 \(\sqrt k\) 級別的。總複雜度 \(\mathcal O(n\sqrt n)\)。
題目
解法
\(\text{40 pts}\)
從小到大放置 \(n\) 個數,對於第 \(i\) 個數就有 \(i\) 個位置放置,貢獻值域為 \([0,i-1]\)。
令 \(dp_{i,j}\) 為前 \(i\) 個數形成 \(j\) 個逆序對的方案數。就有:
\[dp_{i,j}=\sum_{k=0}^{i-1} dp_{i-1,j-k} \]\(\text{60 pts}\)
\[dp_{i,j}-dp_{i,j-1}=dp_{i-1,j}-dp_{i-1,j-i} \]\(\text{100 pts}\)
問題可以轉換成這樣的形式:求滿足
\[\sum_{i=1}^n x_i=k,x_i\le i-1 \]的解的方案數。
可以欽定 \(m\) 個 \(x_i\) 不滿足性質,設這些 \(x_i\) 的 \(i\) 之和為 \(s\)(\(x_i\) 值為 \(i\) 恰好不滿足性質)。那麼方案數就是 \(\binom{n-1}{k+n-1-s}\),容斥係數是 \((-1)^m\)。
這顯然過不去,但其實我們並不關心是哪些 \(x_i\) 超過限制,我們關心的是 \(s\) 和 \(m\)。
那麼令 \(dp_{i,j}\) 為選 \(i\) 個數,它們的和為 \(j\) 的方案數。由於這 \(i\)
- 將所選數整體 \(+1\)。\(dp_{i,j}=dp_{i,j-i}\)。
- 將所選數整體 \(+1\),再加入一個值為 \(1\) 的數。\(dp_{i,j}=dp_{i,j}+dp_{i-1,j-i}\)。
- 有數字加出了 \(n\),需要減去。\(dp_{i,j}=dp_{i,j}-dp_{i-1,j-(n+1)}\)。
現在就是 \(\mathcal O(n^2)\) 的咯?實際上,由於 \(k\) 的限制,我們並不需要選這麼多數。考慮最節省的情況就是連續的等差數列,設項數為 \(x\):
\[\frac{(1+x)\cdot x}{2}=k \]這樣 \(x\)
程式碼
#include <cstdio>
#define print(x,y) write(x),putchar(y)
template <class T>
inline T read(const T sample) {
T x=0; char s; bool f=0;
while((s=getchar())>'9' or s<'0')
f|=(s=='-');
while(s>='0' and s<='9')
x=(x<<1)+(x<<3)+(s^48),
s=getchar();
return f?-x:x;
}
template <class T>
inline void write(const T x) {
if(x<0) {
putchar('-'),write(-x);
return;
}
if(x>9) write(x/10);
putchar(x%10^48);
}
#include <cmath>
const int mod=1e9+7,maxn=1e5+5;
int n,k,fac[maxn<<1],ifac[maxn<<1];
int dp[600][maxn];
int qkpow(int x,int y) {
int r=1;
while(y) {
if(y&1) r=1ll*r*x%mod;
x=1ll*x*x%mod; y>>=1;
}
return r;
}
void init() {
fac[0]=1;
for(int i=1;i<=n+k;++i)
fac[i]=1ll*fac[i-1]*i%mod;
ifac[n+k]=qkpow(fac[n+k],mod-2);
for(int i=n+k-1;i>=0;--i)
ifac[i]=1ll*ifac[i+1]*(i+1)%mod;
}
int C(int n,int m) {
if(n<m or n<0 or m<0)
return 0;
return 1ll*fac[n]*ifac[m]%mod*ifac[n-m]%mod;
}
int main() {
n=read(9),k=read(9); init();
int m=sqrt(k*2)+5;
dp[0][0]=1;
for(int i=1;i<=m;++i)
for(int j=i;j<=k;++j) {
dp[i][j]=(dp[i][j-i]+dp[i-1][j-i])%mod;
if(j>=n+1)
dp[i][j]=(dp[i][j]-dp[i-1][j-(n+1)]+mod)%mod;
}
int ans=C(n+k-1,n-1);
for(int i=1;i<=k;++i) {
int tmp=0;
for(int j=1;j<=m and j<=n;++j)
if(j&1) tmp=(tmp-dp[j][i]+mod)%mod;
else tmp=(tmp+dp[j][i])%mod;
ans=(ans+1ll*C(k+n-1-i,n-1)*tmp%mod)%mod;
}
print(ans,'\n');
return 0;
}