1. 程式人生 > >【CF1445D】Divide and Sum 題解

【CF1445D】Divide and Sum 題解

[題目連結](https://codeforces.ml/contest/1445/problem/D) # 題意簡介 將一個長度為 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$ 時,之前選了 $k$ 個數在**數列 q** 中,$i-1-k$ 個數在 p 中的情形。 (為避免重複計算與討論的麻煩,不妨假設排序時,對於值相同的元素,在 $\{a_i\}$ 中的下標越小越小。) 於是我們知道,前 i-1 個數都比 $a_i$ 小。 由於我們從左到右選數,我們不難看出,每選到一個數加入數列 q,這個數將從右往左地新增到 q 中。而如果是加入數列 p,這個數將被從左往右地加入 p 中。如下圖: ![解釋1](https://img2020.cnblogs.com/blog/2160110/202011/2160110-20201102201108234-218980374.png) 接下來我們分析,假定我們希望將 $a_i$ 選入 **p** 中,那麼 $a_i$ 對應的實際上就是 $x_{i-1-k}$,想要這個數對答案的貢獻為**正**,我們就需要使其對應的 $y_{i-1-k}$ 比它小。由於我們已經將原數列排序,所以這個 $y_{i-1-k}$ 在原數列中對應的 $a_j$ 應有 $j n$ 這樣一條與 k 無關的式子。 換句話說,只要滿足 $i > n$ ,任何的將 $a_i$ 放在 $p$ 的情形,$a_i$ 對答案的貢獻都是正的。反之,貢獻為負。 同理,假如我們考慮把 $a_i$ 放到 **q** 中,同理,假如我們希望其貢獻為正,那麼 $a_i$ 對應的 $y_j$ 所對應的 $x_j$ 所對應的 $a_l$ 的下標應該出現在 i 之前,也就是比 $a_i$ 小。 上面這句話可能有點繞。如下圖。 ![解釋3](https://img2020.cnblogs.com/blog/2160110/202011/2160110-20201102201130750-1537830948.png) 顯然,只有當 $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)$ # 程式碼庫 ```cpp #include #include 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; } ```