【CF1445D】Divide and Sum 題解
題意簡介
將一個長度為 2n 的數列平均分為兩個子數列 p 和 q 後,p 按從小到大排序,q 按從大到小排序。
排序後,記 p 為 \(\{x_i\}\) ,q 為 \(\{y_i\}\) ,對每種劃分方式,有 \(f(p,q) = \sum |x_i - y_i|\)
現在我們想要知道對所有的劃分方案 \((p,q)\) ,\(\sum f(p,q)\) 是多少。
資料範圍:\(1 \leq n \leq 150000\) 答案對 998244353 取模。
Two partitions of an array are considered different if the sets of indices of elements included in the subsequence p are different.
這句話可以這麼理解,就算元素的值相同,只要它們在原數列中的下標不同,就算為不同的元素。
只要原列組中有一個元素的所處位置( p 或 q )不同,就視為兩種劃分方式不同。
思路分析
考慮暴力,我們會發現我們共需要討論 \({2n \choose n}\) 種情況,顯然不能這麼算。
(上面那個是組合數公式 2n 選 n)
於是我們自然而然地想到,既然對每種劃分情況行不通,我們就考慮把每個數分開來,討論其對於答案的貢獻。
通過對式子的觀察,我們可以得出結論:\(x_i,y_i\) 中較大值對答案貢獻為正,較小值對答案貢獻為負。
首先對原數列做排序處理。
現在我們對原數列進行從小到大排序,考慮從左到右選到第 i 個數 \(a_i\)
(為避免重複計算與討論的麻煩,不妨假設排序時,對於值相同的元素,在 \(\{a_i\}\) 中的下標越小越小。)
於是我們知道,前 i-1 個數都比 \(a_i\) 小。
由於我們從左到右選數,我們不難看出,每選到一個數加入數列 q,這個數將從右往左地新增到 q 中。而如果是加入數列 p,這個數將被從左往右地加入 p 中。如下圖:
接下來我們分析,假定我們希望將 \(a_i\) 選入 p 中,那麼 \(a_i\) 對應的實際上就是 \(x_{i-1-k}\),想要這個數對答案的貢獻為正,我們就需要使其對應的 \(y_{i-1-k}\)
而前面的數的選擇我們實際上已經決定好了:我們選了 k 個數在 q 中。所以,我們必須要求這 k 個數中的某個數對於的 \(y_i\) 下標等於 \(i-1-k\) 。如下圖。
顯然,只有當 \(k\geq n-(i-k)+1\) 時,\(a_i\) 對答案的貢獻為正。
經過簡單的化簡,我們得出 \(i > n\) 這樣一條與 k 無關的式子。
換句話說,只要滿足 \(i > n\) ,任何的將 \(a_i\) 放在 \(p\) 的情形,\(a_i\) 對答案的貢獻都是正的。反之,貢獻為負。
同理,假如我們考慮把 \(a_i\) 放到 q 中,同理,假如我們希望其貢獻為正,那麼 \(a_i\) 對應的 \(y_j\) 所對應的 \(x_j\) 所對應的 \(a_l\) 的下標應該出現在 i 之前,也就是比 \(a_i\) 小。
上面這句話可能有點繞。如下圖。
顯然,只有當 \(i-1-k \geq n-1-k+1\) 時,\(a_i\) 對答案的貢獻為正。
化簡後,我們又得到了同一條式子:\(i > n\) 。
於是,我們得出結論,無論怎麼分,只要 \(i>n\) ,\(a_i\) 對答案的貢獻就是正的,反之則是負的。
所以,答案就是 \({2n \choose n} \times (\sum_{i=n+1}^{2n} a_i - \sum_{i=1}^{n} a_i)\)
程式碼庫
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
#define REG register
#define rep(i,a,b) for(REG int i=a;i<=b;i++)
const int N=3e5+5,mod=998244353;
int A[N],n; ll fac[N],ans1,ans2;
inline ll _pow(ll x,int p){
REG ll ans=1;
while(p) (p&1)&&(ans=ans*x%mod),x=x*x%mod,p>>=1;
return ans;
}
inline ll _inv(ll x){
return _pow(x,mod-2);
}
inline ll C(ll a,ll b){
return fac[a]*_inv(fac[b])%mod*_inv(fac[a-b])%mod;
}
int main(){
scanf("%d",&n);
rep(i,1,n<<1) scanf("%d",A+i);
sort(A+1,A+(n<<1)+1);
fac[0]=1;
rep(i,1,n<<1) fac[i]=fac[i-1]*i%mod;
ll temp=C(n<<1,n);
rep(i,1,n) ans1=(ans1+A[i]*temp)%mod;
rep(i,n+1,n<<1) ans2=(ans2+A[i]*temp)%mod;
printf("%lld\n",(-ans1+ans2+mod)%mod);
return 0;
}