A. 小红的双排列

签到,\(1-n\) 每个数输出两次即可。

code:

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
#include <bits/stdc++.h>

#define x first
#define y second
#define endl '\n'

using namespace std;

using ll = long long;
using i128 = __int128;
using PII = pair<int, int>;

i128 read(){i128 x = 0, f = 1;char ch = getchar();while(ch < '0' || ch > '9'){if (ch == '-') f = -1;ch = getchar();}while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}return x * f;}
void print(i128 x){if (x < 0){putchar('-');x = -x;}if(x > 9) print(x / 10);putchar(x % 10 + '0');}

void solve()
{
int n;
cin >> n;

for (int i = 1; i <= n; i++) cout << i << ' ' << i << ' ';
cout << endl;
}

int main()
{
ios::sync_with_stdio(false);
cin.tie(0);

int T = 1;
// cin >> T;
while (T--)
{
solve();
}

return 0;
}

B. 小红的双排列拆分

先后进行两次 "从数组出取出 \(n\) 个互不相同的数" 的操作。

code:

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
#include <bits/stdc++.h>

#define x first
#define y second
#define endl '\n'

using namespace std;

using ll = long long;
using i128 = __int128;
using PII = pair<int, int>;

i128 read(){i128 x = 0, f = 1;char ch = getchar();while(ch < '0' || ch > '9'){if (ch == '-') f = -1;ch = getchar();}while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}return x * f;}
void print(i128 x){if (x < 0){putchar('-');x = -x;}if(x > 9) print(x / 10);putchar(x % 10 + '0');}

void solve()
{
int n;
cin >> n;

vector<int> a(n * 2);
for (int i = 0; i < n * 2; i++) cin >> a[i];

vector<int> st1(n + 1, 0), st2(n * 2, 0);
for (int i = 0; i < n * 2; i++)
{
if (st1[a[i]]) continue;
st1[a[i]] = st2[i] = 1;
cout << a[i] << ' ';
}
cout << endl;
for (int i = 0; i < n * 2; i++)
{
if (st2[i]) continue;
cout << a[i] << ' ';
}
}

int main()
{
ios::sync_with_stdio(false);
cin.tie(0);

int T = 1;
// cin >> T;
while (T--)
{
solve();
}

return 0;
}

C.小红的双排列删除

每次以当前数组最左边的下标作为 \(l\),如果无法找到对应的满足 \(a_l=a_r\) 的右边界 \(r\),代表无法全部删除,输出 \(No\)

code:

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
#include <bits/stdc++.h>

#define x first
#define y second
#define endl '\n'

using namespace std;

using ll = long long;
using i128 = __int128;
using PII = pair<int, int>;

i128 read(){i128 x = 0, f = 1;char ch = getchar();while(ch < '0' || ch > '9'){if (ch == '-') f = -1;ch = getchar();}while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}return x * f;}
void print(i128 x){if (x < 0){putchar('-');x = -x;}if(x > 9) print(x / 10);putchar(x % 10 + '0');}

void solve()
{
int n;
cin >> n;

vector<int> a(n * 2);
for (int i = 0; i < n * 2; i++) cin >> a[i];

int last = 0;
for (int i = 1; i < n * 2; i++)
{
if (a[i] != a[last]) continue;
last = ++i;
}

if (last != n * 2) cout << "No" << endl;
else cout << "Yes" << endl;
}

int main()
{
ios::sync_with_stdio(false);
cin.tie(0);

int T = 1;
cin >> T;
while (T--)
{
solve();
}

return 0;
}

D. 小红的双排列权值

首先预处理出初始数组的贡献 \(res\)

通过模拟可以发现: * 当选择的两个下标 \(l,r\) 对应的两个区间相交时。交换不会使得贡献变大。 * 当选择的两个下标 \(l,r\) 对应的两个区间不相交时。设这两个区间分别为 \((l_1,r_1)\)\((l_2,r_2)\),此时 \(l,r\) 的组合有 \((l=l_1,r=l_2)\)\((l=l_1,r=r_2)\)\((l=l_2,r=l_1)\)\((l=l_2,r=r_2)\),四种情况。经过模拟,发现无论是哪种情况,交换 \(l,r\) 都会使得贡献增加 \((l_2-r_1) \times 2\)

因此只需要找到所有区间中最左的右边界 \(R\),以及最右的左边界 \(L\),如果 \(L > R\),使贡献加上 \((L - R) \times 2\)

最后输出 \(res\) 即可。

code:

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
#include <bits/stdc++.h>

#define x first
#define y second
#define endl '\n'

using namespace std;

using ll = long long;
using i128 = __int128;
using PII = pair<int, int>;

i128 read(){i128 x = 0, f = 1;char ch = getchar();while(ch < '0' || ch > '9'){if (ch == '-') f = -1;ch = getchar();}while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}return x * f;}
void print(i128 x){if (x < 0){putchar('-');x = -x;}if(x > 9) print(x / 10);putchar(x % 10 + '0');}

void solve()
{
int n;
cin >> n;

vector<int> a(n * 2);
vector<PII> st(n + 1, {-1, -1});
for (int i = 0; i < n * 2; i++)
{
cin >> a[i];
if (st[a[i]].x == -1) st[a[i]].x = i;
else st[a[i]].y = i;
}

ll res = 0;
for (int i = 1; i <= n; i++) res += st[i].y - st[i].x - 1;

int L = -1, R = -1;
for (int i = 0; i < n * 2; i++)
{
if (st[a[i]].y == i && R == -1) R = i;
if (st[a[i]].x == i) L = i;
}
if (L > R) res += (L - R) * 2;

cout << res << endl;
}

int main()
{
ios::sync_with_stdio(false);
cin.tie(0);

int T = 1;
// cin >> T;
while (T--)
{
solve();
}

return 0;
}

E. 小红的双排列删除得分

dp。

\(f_i\) 为对数组前 \(i\) 个元素进行删除,所能获得的最高分数,前 \(i\) 个数的元素和为 \(pre_i\), \(a_i\) 在数组中的下标分别为 \(\{st_{a_i}.x,st_{a_i}.y\}\)

可以列出状态转移方程

\[ \begin{aligned} f[i] &= f[i - 1] \\ \text{if } st[a[i]].y = i,\quad &f[i] = \max\left(f[i],\ f[st[a[i]].x - 1] + \text{pre}[i] - \text{pre}[st[a[i]].x - 1]\right) \end{aligned} \]

code:

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
#include <bits/stdc++.h>

#define x first
#define y second
#define endl '\n'

using namespace std;

using ll = long long;
using i128 = __int128;
using PII = pair<int, int>;

i128 read(){i128 x = 0, f = 1;char ch = getchar();while(ch < '0' || ch > '9'){if (ch == '-') f = -1;ch = getchar();}while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}return x * f;}
void print(i128 x){if (x < 0){putchar('-');x = -x;}if(x > 9) print(x / 10);putchar(x % 10 + '0');}

void solve()
{
int n;
cin >> n;

vector<int> a(n * 2 + 1, 0);
vector<PII> st(n + 1, {-1, -1});
for (int i = 1; i <= n * 2; i++)
{
cin >> a[i];
if (st[a[i]].x == -1) st[a[i]].x = i;
else st[a[i]].y = i;
}

vector<ll> pre(n * 2 + 1, 0);
for (int i = 1; i <= n * 2; i++) pre[i] = pre[i - 1] + a[i];

vector<ll> f(n * 2 + 1, 0);
for (int i = 1; i <= n * 2; i++)
{
f[i] = f[i - 1];
if (st[a[i]].y == i) f[i] = max(f[i], f[st[a[i]].x - 1] + pre[i] - pre[st[a[i]].x - 1]);
}

cout << f[n * 2] << endl;
}

int main()
{
ios::sync_with_stdio(false);
cin.tie(0);

int T = 1;
// cin >> T;
while (T--)
{
solve();
}

return 0;
}

F. 小红的双排列期望(easy)

小红的最优策略一定是尽可能少的操作概率小的数,因此可以先对 \(b\) 数组从小到大进行排序,排序后对应的 \(a\) 数组应为 \(1,1,2,2,3,3,...,n,n\)

接下来对于 \(a\) 数组中的每个数,考虑 \(dp\)

假设要把当前的数操作成 \(x\),每次操作成功的概率为 \(p\)\(f_i\) 代表从 \(i\) 操作到 \(x\) 的期望步数,则可以列出状态转移方程

\[ \begin{aligned} f_i = p \times f_{i + 1} + (1 - p) \times f_i + 1 \end{aligned} \]

经过移项,最终变为

\[ \begin{aligned} f_i = f_{i + 1} + \frac{1}{p} \end{aligned} \]

最终可化简为

\[ \begin{aligned} f_i = \frac{x - i}{p} \end{aligned} \]

因此对于数组 \(a\) 中的每个元素,期望步数为 \(\frac{x_i}{p_i}\),全部加起来,即为最终期望步数。

code:

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
#include <bits/stdc++.h>

#define x first
#define y second
#define endl '\n'

using namespace std;

using ll = long long;
using i128 = __int128;
using PII = pair<int, int>;

i128 read(){i128 x = 0, f = 1;char ch = getchar();while(ch < '0' || ch > '9'){if (ch == '-') f = -1;ch = getchar();}while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}return x * f;}
void print(i128 x){if (x < 0){putchar('-');x = -x;}if(x > 9) print(x / 10);putchar(x % 10 + '0');}

constexpr int mod = 1e9 + 7;

void solve()
{
int n;
cin >> n;

vector<int> a(n * 2);
for (int i = 0; i < n * 2; i++) cin >> a[i];

sort(a.begin(), a.end());

auto qmi = [&](ll a, int b)
{
ll res = 1;
while (b)
{
if (b & 1) res = res * a % mod;
b >>= 1;
a = a * a % mod;
}
return res;
};

ll res = 0;
for (int i = 0; i < n * 2; i++)
{
ll x = i / 2 + 1;
ll p = (ll)a[i] * qmi(100, mod - 2) % mod;
res = (res + x * qmi(p, mod - 2) % mod) % mod;
}

cout << res << endl;
}

int main()
{
ios::sync_with_stdio(false);
cin.tie(0);

int T = 1;
// cin >> T;
while (T--)
{
solve();
}

return 0;
}

G. 小红的双排列期望(hard)

同样考虑 \(dp\),状态转移方程为

\[ \begin{aligned} f_i = p \times f_{i + 1} + (1 - p) \times f_{i - 1} + 1 \end{aligned} \]

对于 \(f_0\),需要另列公式

\[ \begin{aligned} f_0 = p \times f_{1} + (1 - p) \times f_{0} + 1 \\ \end{aligned} \]

\[ \begin{aligned} \Downarrow\\ \end{aligned} \]

\[ \begin{aligned} f_0 = f_1 + \frac{1}{p} \end{aligned} \]

再把 \(f_0\) 代入到 \(f_1\) 中可得

\[ \begin{aligned} f_1 = p \times f_{2} + (1 - p) \times (f_1 + \frac{1}{p}) + 1 \\ \end{aligned} \]

\[ \begin{aligned} \Downarrow \\ \end{aligned} \]

\[ \begin{aligned} f_1 = f_2 + \frac{1 - p}{p} + \frac{1}{p} \end{aligned} \]

多次代入归纳整理可得

\[ \begin{aligned} f_0 = \sum_{i=1}^n{\sum_{j=1}^i{\frac{(1-p)^{j-1}}{p^j}}} \\ \end{aligned} \]

预处理一下即可。

code:

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
#include <bits/stdc++.h>

#define x first
#define y second
#define endl '\n'

using namespace std;

using ll = long long;
using i128 = __int128;
using PII = pair<int, int>;

i128 read(){i128 x = 0, f = 1;char ch = getchar();while(ch < '0' || ch > '9'){if (ch == '-') f = -1;ch = getchar();}while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}return x * f;}
void print(i128 x){if (x < 0){putchar('-');x = -x;}if(x > 9) print(x / 10);putchar(x % 10 + '0');}

constexpr int mod = 1e9 + 7;

void solve()
{
int n;
cin >> n;

vector<int> a(n * 2);
for (int i = 0; i < n * 2; i++) cin >> a[i];

sort(a.begin(), a.end());

auto qmi = [&](ll a, int b)
{
ll res = 1;
while (b)
{
if (b & 1) res = res * a % mod;
b >>= 1;
a = a * a % mod;
}
return res;
};

vector f(101, vector<ll>(n + 1, 0));
for (int i = 1; i <= 100; i++)
{
ll sum = 0;
ll p = (ll)i * qmi(100, mod - 2) % mod;
vector<ll> c(n + 1, 1);
for (int j = 1; j <= n; j++) c[j] = c[j - 1] * p % mod;
for (int j = 1; j <= n; j++)
{
sum = (sum + qmi(1 - p + mod, j - 1) * qmi(c[j], mod - 2) % mod) % mod;
f[i][j] = (f[i][j - 1] + sum) % mod;
}
}

ll res = 0;
for (int i = 0; i < n * 2; i++)
res = (res + f[a[i]][i / 2 + 1]) % mod;

cout << res << endl;
}

int main()
{
ios::sync_with_stdio(false);
cin.tie(0);

int T = 1;
// cin >> T;
while (T--)
{
solve();
}

return 0;
}