Codeforce 1445D Divide and Sum
链接
http://codeforces.com/contest/1445/problem/D
题目大意
(更的有点晚。。。)
给你一个2n长度的数组,把它任意分解成两个长度为n的数组p,q。
我们令
求所有分解方法的f(p,q)的和对998244353取模。
输入输出
input:
1
1 4
output:
6
input:
2
2 1 2 1
output:
12
input:
3
2 2 2 2 2 2
output:
0
input:
5
13 8 35 94 9284 34 54 69 123 846
output:
2588544
数据范围
1<=n<=150000
1<=ai<=1e9
说明
对于样例2,
1 2 1 2 这4个数显然有C(4,2) = 6 种分解方法:
【2,1】【2,1】 ,f = 2
【1,1】【2,2】, f = 2
【2,1】【1,2】, f = 2
【1,2】【2,1】, f = 2
【1,1】【2,2】, f = 2
【2,1】【2,1】, f = 2
2+2+2+2+2+2 = 12;
思路
我们很容易发现,对于每一种分解方法,f(p,q)的结果都是
让数组中大的一半减去小的一半。
可以这样简单地证明一下:
我们先令a2n 有序:
则f(p,q) = a2n+a2n-1+a2n-2+…+an+1 - an-an-1-…-a1 @
令集合A = {a2n,a2n-1,a2n-2,…,an+1},即大的半边
集合B = {an,an-1,…,a1} 即小的半边,则f(p,q) = sumA-sumB;
现在我们对@式进行一些变形:
1.我们令B集合中的ai与A集合中的ai+n互换。
例如我们把a2n-an 换成 an-a2n.那么因为加了绝对值,显然总和是不变的
2.那么如果我们把B集合中的ai与A集合中的aj+n互换呢?
我们发现,原式 = |ai+n-ai| + |aj+n-aj| = |aj+n-ai|+|ai+n-aj| =ai+n+aj+n- ai-aj.
所以,得出结论:我们任意地将B集合中的元素换到A中,得到的f(p,q)始终等于sumA-sumB。
到这里本题就变得很简单了:
1.对a2n排序
2.sum(大的半边)- sum(小的半边)
3.求组合数C(2n,n)
求组合数时注意由于需要大数据取模,应该采用快速幂+逆元的做法。(记得取模)
代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll maxn = 150005;
const ll p = 998244353;
ll fac[2*maxn+5];
ll a[maxn*2+10];
ll quick_pow(ll x, ll n, ll mod){
ll res = 1;
while (n!=0){
if (n & 1){
res = res * x % mod;
}
x = x * x % mod;
n >>= 1;
}
return res;
}
ll solve(ll n,ll m){
fac[0] = 1;
for (int i = 1; i <= n; i++){
fac[i] = fac[i - 1] * i % p;
}
return fac[n] * quick_pow(fac[m], p - 2, p) % p * quick_pow(fac[n - m], p - 2, p) % p;
}
int main()
{
int n;
cin>>n;
for(int i=0;i<2*n;i++){
scanf("%lld",&a[i]);
}
sort(a,a+2*n);
ll ans = 0;
for(int i=0;i<n;i++){
ans = (ans + a[i+n] - a[i]) % p;
}
cout<<ans*solve(2*n,n)%p<<endl;
return 0;
}