题面:

B - Gold Medal

签到题,很显然先分配那些补较少人数就能产生牌子的比赛,那么补全了之后如果还有剩的直接除k向下取整就行。

1
2
3
4
5
6
7
8
9
10
11
12
13
void solve() {
int n,k;
cin>>n>>k;
vector<int> a(n);
int ans = 0;
for(int i = 0;i<n;++i) cin>>a[i],ans += a[i] / k,a[i] %= k;
int m,p = 0;
cin>>m;
sort(all(a),greater());
while(p < n && m - (k - a[p]) >= 0) ans++,m -= (k - a[p++]);
ans += m / k;
cout<<ans<<endl;
}

G - Be Positive

观察样例可以发现当n == 4 || n == 1的时候归零了。

首先不难看出0,1,2,3;4,5,6,7;8,9,10,11;这样每四个连续的数异或都是0。

所以可以假设n是4的倍数<=>无解的(等于0)。

那么想让每一个前缀都大于0就要对其偏移一位就行,比如n == 2的时候是先1后0,即1 0,其他情况直接顺序排就行。

用队列实现比较容易一些。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void solve() {
int n;
cin>>n;
if(n % 4 == 0 || n == 1) return cout<<"impossible\n",void();
queue<int> q;
for(int i = 0;i<n;++i){
if((i + 1) % 4 == 0 || i == 0){
q.push(i);
continue;
}
cout<<i<<' ';
if(!q.empty()) cout<<q.front()<<' ',q.pop();
}
cout<<endl;
}

I - Left Shifting 2

左移操作最优的情况就是把一段相同字符拆成两段,如果一个偶数拆成了两个奇数,那么答案能减小1,只有奇数不影响。

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
void solve() {
string s;
cin >> s;
int n = si(s);
if (n == 1) return cout << 0 << endl, void();
vector<int> a;
// 双指针求每个连续的段的长度
for (int i = 0, j = 1; j <= n; ++j) {
if (j == n) {
a.push_back(j - i);
} else {
while (s[i] == s[j]) j++;
a.push_back(j - i);
i = j;
}
}

// 注意都是一个字母的情况就无所谓左移了
if(si(a) == 1) return cout<<a[0] / 2<<endl,void();

// ok表示存不存在偶数长度的连续段
int ok = 0;

// 先特判头尾都是一样的字母
if (s.front() == s.back() && !a.empty()) {
a.front() += a.back();
a.pop_back();
}
int ans = 0;
for (auto i : a) ans += i >> 1,ok |= (i % 2 == 0);
cout << ans - ok << endl;
}

A - Two-star Contest

直接贪心就行,注意题目没有说每个比赛的评级是不一样的,所以可能有评级一样的比赛,但是分数不一样,下一评级的比赛必须要大于这一级比赛的最大的分数。

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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
void solve() {
int n, m, k;
cin >> n >> m >> k;
vector<int> s(n), p;
vector<vector<int>> info(n, vector(m, 0ll));
for (int i = 0; i < n; ++i) {
p.push_back(i);
for (int j = -1; j < m; ++j) {
if (j == -1) cin >> s[i];
else cin >> info[i][j];
}
}
sort(all(p), [&](int x, int y) {
return s[x] < s[y];
});
int pre = -1, prep = -1;
int maxn = pre;
// cerr<<pre<<endl;
for (int i : p) {
int tot, cur = 0;

if(prep != s[i]) pre = maxn;

tot = pre + 1;

for (auto &j : info[i]) if(j != -1) cur += j;
for (auto &j : info[i]) {
if (j == -1) {
j = cur >= tot ? 0ll : min(k, tot - cur);
cur += j;
}
}
if (tot > cur) return cout << "No" << endl, void();
maxn = max(maxn, cur);
prep = s[i];
}
cout << "Yes" << endl;
for (int i = 0; i < n; ++i)
for (int j = 0; j < m; ++j) cout << info[i][j] << " \n"[j == m - 1];
}
/*
队友的写法

struct Info {
int q, l, r, v, pos;
} Data[N];

int n, m, k;

vector<int> p[N];

void solve() {
cin >> n >> m >> k;
for (int i = 1; i <= n; ++i) {
cin >> Data[i].q;
for (int j = 0; j < m; ++j) {
int t;
cin >> t;
p[i].push_back(t);
}
}

for (int i = 1; i <= n; ++i) {
Data[i].pos = i;
}

for (int i = 1; i <= n; ++i) {
for (auto j : p[i]) {
if (j == -1) {
Data[i].r += k;
} else {
Data[i].l += j;
Data[i].r += j;
}
}
}

sort(Data + 1, Data + 1 + n, [&](Info x, Info y) {
return x.q < y.q;
});

Data[1].v = Data[1].l;
int x = Data[1].l;
int pre = 0;

for (int i = 2; i <= n; ++i) {
if (Data[i].q != Data[i - 1].q) {
pre = x + 1;
x = 0;
if (Data[i].r >= pre) {
Data[i].v = max(Data[i].l, pre);
x = max(x, Data[i].v);
} else {
for(int i = 1;i<=n + 5;++i) Data[i].r = Data[i].v = Data[i].q = Data[i].l = Data[i].pos = 0;
for (int i = 1; i <= n; ++i) p[i].clear();
return cout << "No" << endl, void();
}
} else {
if (Data[i].r >= pre) {
Data[i].v = max(Data[i].l, pre);
x = max(x, Data[i].v);
} else {
for(int i = 1;i<=n + 5;++i) Data[i].r = Data[i].v = Data[i].q = Data[i].l = Data[i].pos = 0;
for (int i = 1; i <= n; ++i) p[i].clear();
return cout << "No" << endl, void();
}
}
}
cout << "Yes" << endl;
sort(Data + 1, Data + 1 + n, [&](Info x, Info y) {
return x.pos < y.pos;
});
// for(int i = 1;i<=n;++i){
// cout<<Data[i].v<<' ';
// }
// cout<<endl;


for (int i = 1; i <= n; ++i) {
int temp = 0;
for (auto j : p[i]) temp += max(j, 0ll);
temp = Data[i].v - temp;
for (auto &j : p[i]) {
if (j == -1 && temp > 0) {
j = min(temp, k);
temp -= j;
}
}
}
for (int i = 1; i <= n; ++i) {
for (auto &j : p[i]) {
if(j == -1) j = 0;
cout << j << " ";
}
cout << endl;
p[i].clear();
}

for(int i = 1;i<=n + 5;++i) Data[i].r = Data[i].v = Data[i].q = Data[i].l = Data[i].pos = 0;

}


*/

致敬传奇二星级比赛ICPC。

E - Relearn through Review

问你一个长度为n的正整数序列,可以选一个连续的子序列(可以为空)加上k,k为非负整数,问你这样操作之后这个序列整个的gcd最大是多少。

首先想一下最暴力的解法。

枚举左右端点,这个区间内的所有数加k。那么最坏至少是$ O(n^2) $的。

考虑优化,把这个前后缀打表,如第一个样例的数据:

1
2
3
4
5
6
6 2
5 3 13 8 10 555
pre:
5 1 1 1 1 1 1
suf:
1 1 1 1 5 555

可以发现其实有很多都是重复的数,其实最大大致只有logN个,N是这个数组中具有最多因子数的数。

前缀一定是单调递减的,因为重复的数一定是连续的,那么贪心的想,选每次即将变小的位置作为加k的左端点一定是当前这个gcd最优的情况。因为这能尽可能地保证后缀是最大的。因为最多是log级别的,所以每固定一个l,然后枚举r。暴力跑一遍大致就是$ O(nlogN) $的。

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
void solve() {
int n, k;
cin >> n >> k;
vector<int> a(n);
for (int i = 0; i < n; ++i) cin >> a[i];
vector<int> suf(n + 2), pre(n + 1);
// pre[0] = 0, suf[n] = suf[n+1] = a[n - 1];
for (int i = 0; i < n; ++i) pre[i + 1] = gcd(pre[i], a[i]);
for (int i = n; i > 0; --i) suf[i] = gcd(suf[i + 1], a[i-1]);

// for(int i = 1;i<=n;++i) cerr<<pre[i]<<" \n"[i == n];
// for(int i = 1;i<=n;++i) cerr<<suf[i]<<" \n"[i == n];

// 特判所有前后缀gcd都是一样的时候
/*
in:
5 2
5 10 10 10 5
out:
5
*/
// int maxn = 0;
int maxn = pre[n];

for(int i = 1;i<=n;++i){
// gcd前缀种类最多是log个,且单调递减
// 那么贪心的想当当前这个位置就要变小时前缀gcd一定是最大的,对答案的影响一定不劣
if(pre[i] != pre[i - 1]){
int g = pre[i-1];
// 枚举右区间
for(int j = i;j<=n;++j){
g = gcd(g,a[j-1] + k);
maxn = max(maxn,gcd(g,suf[j+1]));
}
// cerr<<i<<' '<<maxn<<endl;
}
}
cout<<maxn<<endl;
}