G. Game of Swapping Numbers

题目描述

Given two arrays AA, BB with length NN, you should perform exactly KK operations in array AA.

For each operation, choose 22 elements AiA_i, AjA_j (1i<jN1≤i<j≤N​) and swap the positions of AiA_i and AjA_j.

Please maximize i=1nAiBi\sum\limits_{i = 1}^{n}|A_i-B_i|.

输入描述

The first line of input contains two integers N,KN,K (2N5×1052≤N≤5×10^5 ,0K1080≤K≤10^8), describing the length of AA, BB and number of operations.

The second line contains NN integers A1,...,ANA_1,...,A_N (108Ai108−10^8≤A_i≤10^8).

The third line contains NN integers B1,...,BNB_1,...,B_N (108Bi108−10^8≤B_i≤10^8).

输出描述

Output one integer, the maximum answer.

示例1

输入

1
2
3
3 2
1 2 3
3 2 1

输出

1
4

示例2

输入

1
2
3
3 2
1 2 3
1 2 3

输出

1
4

示例3

输入

1
2
3
3 1
1 2 3
3 2 1

输出

1
4

思路

考虑任意一个最优解,我们把交换后的数字重新放回原来的位置,相当于为每个元素分配了在最优解中的符号再相加。只需要满足 AA​ 中正号与 BB​ 中负号相等、AA​ 中负号与 BB​ 中正号即可,进而可以得到只需要 A,BA,B​ 中正负号的总和各为 NN​ 即可。假设我们能任意指定 kk​ 来求最优解,相当于是把 A,BA, B​ 合在一起排序,取最大的 NN​ 个填正号,最小的 NN​ 个填负号即可。

首先考虑如何最少步数得到最优解。考察一对元素 Ai,BiA_i,B_i​,如果它们分配的符号不同,讨论两种情况:若分配的符号与实际相符(比如 Ai>BiA_i>B_i,分配 AiA_i 为正号,BiB_i 为符号),那么无需改动;若与实际不相符(比如 Ai>BiA_i>B_i,分配 AiA_i 为负号,BiB_i 为正号),那么符号分配就不会是最优解,矛盾。也就是说,当 Ai,BiA_i,B_i 分配的负号不同,就直接忽略。如果 Ai,BiA_i,B_i 分配的符号相同,比如是两个正号,就需要分配了两个负号的 Aj,BjA_j,B_j 进行交换。

假设到达最优解只需要 kk' 步,但是题目要求我们需要恰好交换 kk 步。若 k<kk'<k,我们就需要进行 kkk-k' 次无效交换,即到达最优解后,选取符号相同的 AiA_iAjA_j​​​ 进行交换。根据抽屉原理,当 N>2N>2 时,在 AA​​ 中必有两个位置分配的符号相同;当 N=2N=2​ 时我们进行特判。

我们分类讨论也可佐证上述的论证。考察两个数对 (Ai,Bi)(A_i,B_i)(Aj,Bj)(A_j,B_j),交换后贡献的增量为 δ=AjBi+AiBjAiBiAjBj\delta=|A_j-B_i|+|A_i-B_j|-|A_i-B_i|-|A_j-B_j|。若 N>2N>2,必然存在两个位置 i,ji,j,有 Ai>Bi,Aj>BjA_i>B_i,A_j>B_jAi<Bi,Aj<BjA_i<B_i,A_j<B_j,交换后贡献只增不减。若交换后贡献增加,增加的贡献为 2(min{Ai,Bi}max{Aj,Bj})2(\min\{A_i,B_i\}-\max\{A_j,B_j\})2(min{Aj,Bj}max{Ai,Bi})2(\min\{A_j,B_j\}-\max\{A_i,B_i\}),两者其实没有区别。

  • Ai>BiA_i>B_i
    • Aj>BjA_j>B_j
      • Bi>AjB_i>A_jδ=2(BiAj)\delta=2(B_i-A_j)
      • Bi<AjB_i<A_j
        • Ai>BjA_i>B_jδ=0\delta=0
        • Ai<BjA_i<B_jδ=2(BjAi)\delta=2(B_j-A_i)
  • Ai<BiA_i<B_i
    • Aj<BjA_j<B_j
      • Aj>BiA_j>B_iδ=2(AjBi)\delta=2(A_j-B_i)
      • Aj<BiA_j<B_i
        • Ai<BjA_i<B_jδ=0\delta=0
        • Ai>BjA_i>B_jδ=2(AiBj)\delta=2(A_i-B_j)​。

将所有 min{Ai,Bi}\min\{A_i,B_i\}max{Ai,Bi}\max\{A_i,B_i\} 排序。假设 min{Ai,Bi}\min\{A_i,B_i\} 的前 kk 大与 max{Ai,Bi}\max\{A_i,B_i\} 的前 kk 小两个区间没有相交部分,就分别依次取前 kk 大和前 kk 小相减取和即可;若有相交,则取不相交的两端,由于 min{Ai,Bi}\min\{A_i,B_i\} 是正贡献,max{Ai,Bi}\max\{A_i,B_i\} 是负贡献,取相交位置只会使总贡献变少。图中 min{Ai,Bi}\min\{A_i,B_i\}max{Ai,Bi}\max\{A_i,B_i\} 都是降序。

Wyw2If.png

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
/******************************************************************
Copyright: 11D_Beyonder All Rights Reserved
Author: 11D_Beyonder
Problem ID: 2021牛客暑期多校训练营1 G
Date: 2021 June 23rd
Description: Absolute value, Argument
*******************************************************************/
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAX_N = 500004;
int a[MAX_N], b[MAX_N];
int Max[MAX_N], Min[MAX_N];
int n, k;
ll ans;
int main()
{
cin >> n >> k;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
}
for (int i = 1; i <= n; i++)
{
cin >> b[i];
}
if (n == 2)
{
if (k & 1)
{
swap(a[1], a[2]);
}
cout << abs(a[1] - b[1]) + abs(a[2] - b[2]) << endl;
return 0;
}
for (int i = 1; i <= n; i++)
{
Min[i] = min(a[i], b[i]);
Max[i] = max(a[i], b[i]);
ans += abs(a[i] - b[i]);
}
sort(Min + 1, Min + n + 1, greater<int>());
sort(Max + 1, Max + n + 1, less<int>());
for (int i = 1; i <= min(k, n); i++)
{
if (Min[i] > Max[i])
{
ans += 2 * (Min[i] - Max[i]);
}
}
cout << ans << endl;
return 0;
}

H. Hash Function

题目描述

For a given set S={a0,a1,a2,...,an1}S = \{a_0, a_1, a_2, ..., a_{n-1}\}, we called a hash function fS(x)f_S(x) perfect hash function of SS, if it satisfies 0i<j<n\forall 0 \leq i < j < n, fS(ai)fS(aj)f_S(a_i) \neq f_S(a_j).

Given a set S with non-negative integers, please find the minimum positive integer seed that function hseed(x)=xmodseedh_{seed}(x) = x \bmod seed​ is a perfect hash function of SS​.

输入描述

The first line of input contains one integer nn, describing the size of set SS.

The second line contains nn (1n5000001 \leq n \le 500000) integers a0,...,an1a_0,...,a_{n-1}, describing the elements in SS.

It is guaranteed that 0i<j<n,\forall 0 \leq i < j < n, aiaja_i \ne a_j, 0ai5000000 \le a_i \le 500000.

输出描述

Output one integer in a line, indicating the answer.

示例1

输入

1
2
3
2 3 4

输出

1
3

示例2

输入

1
2
3
1 2 4

输出

1
4

示例3

输入

1
2
10
0 7 23 18 29 27 6 28 24 11

输出

1
15

思路

0i<j<n\forall0\le i<j<n​,ai≢aj(mods)a_i\not\equiv a_j\pmod s​;等价于 0i<j<n\forall0\le i<j<n​,s∤(aiaj)s\not\mid(a_i-a_j)​。注意到数据范围较小,集合中任意两数的差属于 (500000,500000)(-500000,500000)​​,可进行多项式优化,用 NTT 或 FTT 得到某个差值是否存在。

定义两个多项式 A,BA,B​​​,系数分别为 ai,bia_i,b_i​​​;两者的卷积为 CC​​​,ck=i+j=kaibjc_k=\sum\limits_{i+j=k}a_ib_j​​​。我们的想法是令 ckc_k​​​ 为差值 kk​​​ 是否存在的标志,那么卷积 \sum​​​ 的限制条件应该为 ij=ki-j=k​​​,但是下标不可能为负数,因此我们需要将 BB​​​ 的系数下标增加一个偏移量 M=500000M=500000​​​。若 iSi\in S​​​,则 ai=1a_i=1​​​,bMi=1b_{M-i}=1​。两个多项式进行卷积运算,就能得到差值是否存在的信息,ck=i+Mj=kaibMjc_k=\sum\limits_{i+M-j=k}a_ib_{M-j};若 ck>0c_k>0,就代表 kMk-M 的差值存在。

我们枚举最终的答案 ss​​​​,根据抽屉原理,ss​​​​ 可以直接从 nn​​​​ 开始枚举。对于当前的模数 ss​​​​​,我们需要快速查询存在的差值中是否有一个差值 sδs|\delta;我们只需要枚举 ss 的所有倍数,检查是否存在 ss 的某个倍数正好是 SS​​​​​ 中两数的差。枚举的时间复杂度是 O(NlogN)O(N\log N)

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
/******************************************************************
Copyright: 11D_Beyonder All Rights Reserved
Author: 11D_Beyonder
Problem ID: 2021牛客多校训练营1 H
Date: 2021 July 24th
Description: NTT, Number Theory
*******************************************************************/
#include<iostream>
using namespace std;
typedef long long ll;
const int SIZE = 1 << 21;
const int MAX = 500000;
const int MOD = 998244353;
int n;
ll a[SIZE], b[SIZE], res[SIZE], rev[SIZE];
int len = 1 << 20;
ll quickPow(ll a, ll b)
{
ll res = 1;
while (b)
{
if (b & 1)
{
res = res * a % MOD;
}
b >>= 1;
a = a * a % MOD;
}
return res;
}
void NTT(ll* a, short opt)
{
for (int i = 0; i < len; i++)
{
if (i < rev[i])
{
swap(a[i], a[rev[i]]);
}
}
for (int k = 2; k <= len; k <<= 1)
{
ll m = k >> 1, x = quickPow(3, (MOD - 1) / k), w = 1, v;
if (opt == -1)
{
x = quickPow(x, MOD - 2);
}
for (int i = 0; i < len; i += k, w = 1)
{
for (int j = i; j < i + m; j++)
{
v = w * a[j + m] % MOD;
a[j + m] = (a[j] - v + MOD) % MOD;
a[j] = (a[j] + v) % MOD;
w = w * x % MOD;
}
}
}
if (opt == -1)
{
ll inv = quickPow(len, MOD - 2);
for (int i = 0; i < len; i++)
{
a[i] = a[i] * inv % MOD;
}
}
}
int main()
{
cin >> n;
for (int i = 1; i <= n; i++)
{
int x;
scanf("%d", &x);
a[x] = 1;
b[MAX - x] = 1;
}
for (int i = 0; i < len; i++)
{
rev[i] = (rev[i >> 1] >> 1) | ((i & 1) ? len >> 1 : 0);
}
NTT(a, 1);
NTT(b, 1);
for (int i = 0; i < len; i++)
{
res[i] = a[i] * b[i] % MOD;
}
NTT(res, -1);
// 所有差是否存在都已经知道
for (int ans = n;; ++ans)
{
bool ok = true;
for (int p = ans; p <= MAX; p += ans)
{
if (res[p + MAX])
{
ok = false;
break;
}

}
// 没必要枚举负数差值,因为两数的两个差互为相反数。
// for (int p = -ans; p >= -MAX; p -= ans)
// {
// if (res[p + MAX])
// {
// ok = false;
// break;
// }
// }
if (ok)
{
cout << ans << endl;
return 0;
}
}
}