题目
http://codeforces.com/problemset/problem/1444/B
http://codeforces.com/contest/1445/problem/D
题目大意
给你一个长度为2n的序列a,你要从中选出两个长度为n的子序列p,q,每个元素只能存在于一个序列中,也必须被选。将p从小到大排序,q从大到小排序,对于每一种选择的方案,定义
f
(
p
,
q
)
=
∑
i
=
1
n
∣
p
i
−
q
i
∣
f(p,q)=\sum_{i=1}^n|p_i−q_i|
f(p,q)=∑i=1n∣pi−qi∣。求
∑
f
\sum f
∑f。
1
≤
n
≤
150000
,
1
≤
a
≤
1
0
9
1\le n\le 150000,1\le a\le 10^9
1≤n≤150000,1≤a≤109
题解
这道题比赛时不会做。想到了把绝对值号拆开,但是不知道一个数什么时候取正,什么时候取负。
赛后,发现不少同学都做出来了(据他们说是“格物致知”出来的)……
原来就是一道结论题。结论是:把元素从小到大排序,前n个只可能取负号,后n个只能取正号。
证明如下(用x和y分别表示 p i p_i pi和 q i q_i qi的下标):
- 若 x x x和 y y y一个大于n,一个小于等于n,显然小的取负,大的取正;
- 若
x
x
x和
y
y
y都小于等于n,当
x
<
y
x<y
x<y时,序列长成这个样子:
a
1
,
a
2
,
a
3
,
⋯
,
a
x
,
⋯
,
a
y
,
⋯
,
a
n
,
a
n
+
1
,
⋯
,
a
2
n
a_1,a_2,a_3,\cdots,a_x,\cdots,a_y,\cdots,a_n,a_{n+1},\cdots,a_{2n}
a1,a2,a3,⋯,ax,⋯,ay,⋯,an,an+1,⋯,a2n用
u
u
u表示
[
1
,
x
]
[1,x]
[1,x]中被p选择的元素个数,那么选择情况就是这样的(为了让x和y相对应,q要在
[
y
,
2
n
]
[y,2n]
[y,2n]中选u个):
可以发现左边 [ 1 , y − 1 ] [1,y-1] [1,y−1]中就选择了至少n个元素了,而 y − 1 < n y-1<n y−1<n,因此这种情况不可能存在;当 x > y x>y x>y时也是同理。故 x x x和 y y y不可能同时小于等于n; - 若 x x x和 y y y都大于等于n,同理于2,舍去。
因此直接 O ( n ) O(n) O(n)算就好了。
CODE
#include<cstdio>
#include<algorithm>
using namespace std;
#define P 998244353
#define N 300005
int fac[N],inv[N],a[N];
int main()
{
int n,m,ans=0;
fac[0]=1;for(int i=1;i<N;++i) fac[i]=1LL*fac[i-1]*i%P;
inv[1]=1;for(int i=2;i<N;++i) inv[i]=1LL*inv[P%i]*(P-P/i)%P;
inv[0]=1;for(int i=1;i<N;++i) inv[i]=1LL*inv[i-1]*inv[i]%P;
scanf("%d",&n),m=n<<1;
for(int i=1;i<=m;++i) scanf("%d",a+i);
sort(a+1,a+m+1);
for(int i=1;i<=m;++i) if(a[i]>=P) a[i]-=P;
for(int i=n+1;i<=m;++i){ans+=a[i];if(ans>=P) ans-=P;}
for(int i=1;i<=n;++i){ans-=a[i];if(ans<0) ans+=P;}
printf("%lld\n",1LL*ans*fac[m]%P*inv[n]%P*inv[n]%P);
return 0;
}