A.Modulo Equality
题意
一个数列同时加上一个数,变成另一个数列(两个数列都是mod m),求最小的那个数
思路
刚开始想到一个通解,用差分+KMP直接进行计算,可是出了点差错,浪费了很多时间,后来就直接暴力求解。
代码
/*
* @Author: Icey_dying
* @Date: 2021-09-24 20:59:31
* @LastEditors: Icey_dying
* @LastEditTime: 2021-09-24 21:18:29
* @FilePath: \Icey_dying\competition\2021\2021.09\2021.9.24\A1.cpp
*/
#include <bits/stdc++.h>
using namespace std;
const int N = 2e3 + 5;
struct sa {
int id;
int num;
};
bool cmp(const sa& a, const sa& b)
{
if (a.num != b.num)
return a.num > b.num;
return a.id < b.id;
}
sa hh1[N], hh2[N];
map<int, int> map1, map2;
int f(int n, int cnt, int m)
{
for (int i = 1; i <= cnt; i++) {
if (map2[(hh1[i].id + n) % m] != hh1[i].num)
return 0;
}
return 1;
}
int n, m;
int a[N], b[N];
int main()
{
cin >> n >> m;
for (int i = 1; i <= n; i++)
cin >> a[i];
for (int i = 1; i <= n; i++)
cin >> b[i];
for (int i = 1; i <= n; i++)
map1[a[i]]++;
for (int i = 1; i <= n; i++)
map2[b[i]]++;
int cnt = 0;
for (auto i : map1)
hh1[++cnt] = { i.first, i.second };
cnt = 0;
for (auto i : map2)
hh2[++cnt] = { i.first, i.second };
sort(hh1 + 1, hh1 + 1 + cnt, cmp);
sort(hh2 + 1, hh2 + 1 + cnt, cmp);
int wow = hh1[1].num, minx = 0x3f3f3f3f;
for (int i = 1; i <= cnt; i++) {
if (hh2[i].num == wow) {
int ke = (hh2[i].id + m - hh1[1].id) % m;
// cout << ke << endl;
if (f(ke, cnt, m))
minx = min(minx, ke);
} else
break;
}
cout << minx << endl;
return 0;
}
B.Maximum Sum on Even Positions
题意
定义一个数组的结果为其下标为偶数的数的和。现在可以让 [ l , r ] [l,r] [l,r]进行反转。求反转一次可以得到的最大结果。
思路
考虑
n
=
5
,
[
1
,
4
]
n=5,[1,4]
n=5,[1,4]反转:
原来:
a
[
0
]
+
a
[
2
]
+
a
[
4
]
a[0]+a[2]+a[4]
a[0]+a[2]+a[4]
反转之后:
a
[
0
]
+
a
[
1
]
+
a
[
3
]
a[0]+a[1]+a[3]
a[0]+a[1]+a[3]
差值为
(
a
[
1
]
−
a
[
2
]
)
+
(
a
[
3
]
−
a
[
4
]
)
(a[1]-a[2])+(a[3]-a[4])
(a[1]−a[2])+(a[3]−a[4])
所以可以把相邻两个看为一个,这题就变成了最大子段和问题,直接dp求解
注意可以1,2和2,3,所以需要两次计算
代码
/*
* @Author: Icey_dying
* @Date: 2021-09-24 21:29:08
* @LastEditors: Icey_dying
* @LastEditTime: 2021-09-27 17:37:26
* @FilePath: \Icey_dying\competition\2021\2021.09\2021.9.24\B.cpp
*/
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
long long a[N], b[N], c[N];
long long dp1[N], dp2[N];
int t, n;
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> t;
while (t--) {
cin >> n;
long long ans = 0;
for (int i = 0; i < n; i++) {
cin >> a[i];
if (!(i % 2))
ans += a[i];
}
for (int i = 1; i < n; i += 2) {
b[(i + 1) / 2] = a[i] - a[i - 1];
}
for (int i = 2; i < n; i += 2) {
c[i / 2] = a[i - 1] - a[i];
}
dp1[0] = dp2[0] = 0;
for (int i = 1; i <= n / 2; i++) {
dp1[i] = max(b[i], b[i] + dp1[i - 1]);
dp2[i] = max(c[i], c[i] + dp2[i - 1]);
}
long long max1 = -1e9, max2 = -1e9;
for (int i = 1; i <= n / 2; i++) {
max1 = max(max1, dp1[i]);
max2 = max(max2, dp2[i]);
}
// cout << "ans=" << ans << endl;
printf("%lld\n", ans + max(0ll, max(max1, max2)));
}
return 0;
}
C.Sasha and a Bit of Relax
题意
给定一个含n个整数的序列a,定义 ( l , r ) (l,r) (l,r)是一个funny pair当且仅当r>l,r-l+1为偶数,且a中下标自l到(l+r-1)/2的数的异或和等于下标自(l+r-1)/2+1到r的数的异或和。求序列a中funny pairs的个数
思路
题目给的其实就是有几个连续偶数个序列的异或值为0,直接计算即可
代码
/*
* @Author: Icey_dying
* @Date: 2021-09-27 17:46:57
* @LastEditors: Icey_dying
* @LastEditTime: 2021-09-27 17:51:29
* @FilePath: \Icey_dying\competition\2021\2021.09\2021.9.24\C.cpp
*/
#include <bits/stdc++.h>
using namespace std;
int n, m;
long long sum = 0, ans = 0;
int a[(1 << 20) + 5][2];
int main()
{
cin >> n;
a[0][0] = 1;
for (int i = 1; i <= n; i++) {
cin >> m;
sum ^= m;
ans += a[sum][i & 1]++;
}
cout << ans << endl;
return 0;
}
E.Walk on Matrix
题意
有这么一个问题:
有一个
n
×
m
n \times m
n×m的矩阵
a
n
,
m
a_{n, m}
an,m,矩阵的每个位置都有一个数,要求寻找一个每次只能向右或向下走的从
(
1
,
1
)
(1, 1)
(1,1)至
(
n
,
m
)
(n, m)
(n,m)的路径,最大化路径上的数的按位与之和。
1
≤
n
,
m
≤
500
,
0
≤
a
i
,
j
≤
3
×
1
0
5
1\leq n, m \leq 500,0 \leq a_{i, j} \leq 3 \times 10^5
1≤n,m≤500,0≤ai,j≤3×105
现在给出解决该问题的一个错误 dp 算法(见题面图片),请构造一组数据,hack 掉这个算法,使得正确答案比错误的输出恰好大k。
思路
首先考虑一下以上的dp错在哪里
我们发现对于AND,也就是按位与操作,不见得一个最大的数字与上某个特定的数字就是最大的,例如:
100000 & 1 = 0 , 1 & 1 = 1
为什么呢?因为不见得大的数字就会和这个特定数字有更多的同为1的二进制位
这个应该很显然
那么我们考虑根据这个东西来Hack他
我们考虑 “骗” 这个dp,让他选择一个看似更大的答案,最后把这个答案给卡掉,就可以让dp的答案为0,最后让这个方案的真正答案为k,差值就是k了。
代码
/*
* @Author: Icey_dying
* @Date: 2021-09-27 17:58:10
* @LastEditors: Icey_dying
* @LastEditTime: 2021-09-27 17:59:34
* @FilePath: \Icey_dying\competition\2021\2021.09\2021.9.24\E.cpp
*/
#include <bits/stdc++.h>
using namespace std;
int k;
int main()
{
cin >> k;
cout << "2 3\n";
cout << (1 << 17) + k << ' ' << (1 << 17) << " 0\n";
cout << k << ' ' << k + (1 << 17) << ' ' << k << endl;
return 0;
}
F.Choosing flowers
题意
有m种物品,每种物品有无限个,你可以购买n个物品。
对于第
i
i
i种物品:
第一次买时的贡献是
a
i
a_i
ai ,接下来每购买一个的贡献都是
b
i
b_i
bi。即当你买了
x
i
x_i
xi个第
i
i
i种物品时,贡献是
a
i
+
b
i
×
(
x
i
−
1
)
a_i+b_i \times (x_i-1)
ai+bi×(xi−1)
现在要你求出最大贡献。
思路
首先很显然的,我们把每一种物品拆成
2
2
2个,一个贡献是
a
i
a_i
ai,一个贡献是
b
i
b_i
bi。然后按贡献排序(显然尽量取贡献大的)。
我们再设
s
o
l
v
e
(
t
,
n
n
)
solve(t,nn)
solve(t,nn)表示从下标为
t
t
t的物品往后取,还能取
n
n
nn
nn个。在遍历的时候分
3
3
3种情况。
如果
i
i
i是第一次买的贡献,那很显然
a
n
s
ans
ans直接加贡献,
n
n
nn
nn减一。
如果
i
i
i是第二次买的贡献且第一次买过了,那剩下
n
n
nn
nn个肯定都买这个了。
如果
i
i
i是第二次买的贡献且第一次没有买,那么我们先算买这种的贡献,然后和不买这种的(即
s
o
l
v
e
(
i
+
1
,
n
n
)
solve(i+1,nn)
solve(i+1,nn))取最大值。
具体第一次是否买过了可以用数组标记。
代码
/*
* @Author: Icey_dying
* @Date: 2021-09-27 18:08:15
* @LastEditors: Icey_dying
* @LastEditTime: 2021-09-27 18:26:11
* @FilePath: \Icey_dying\competition\2021\2021.09\2021.9.24\F.cpp
*/
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
#define ll long long
struct node {
ll x;
int y, id;
} p[N * 2];
int t, m, tot;
ll n, a[N][2];
bool flag[N];
bool cmp(const node& a, const node& b) { return a.x > b.x; }
ll solve(int t, int nn)
{
ll res = 0;
for (int i = t; i <= tot && nn; i++) {
if (p[i].id == 0) {
res += p[i].x;
nn--;
flag[p[i].y] = 1;
} else {
if (flag[p[i].y]) {
res += nn * p[i].x;
break;
} else {
ll val = a[p[i].y][0] + (nn - 1) * p[i].x;
res += max(val, solve(i + 1, nn));
break;
}
}
}
return res;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> t;
while (t--) {
tot = 0;
cin >> n >> m;
for (int i = 1; i <= m; i++) {
cin >> a[i][0] >> a[i][1];
flag[i] = 0;
p[++tot] = (node) { a[i][0], i, 0 };
p[++tot] = (node) { a[i][1], i, 1 };
}
sort(p + 1, p + 1 + tot, cmp);
cout << solve(1, n) << endl;
}
return 0;
}