ICPC Central Russia Regional Contest, 2024
一、A题
模拟,需要考虑两种情况,一种上升后降,一种下降后上升。
cpp
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
int a[N],n;
bool panduanup(const int a[]) {
int f = -1, end = 0 ;
for (int i = 0; i < n - 1; i++) {
if (a[i] < a[i + 1]) {
f = i + 1;
}else break;
}
if (f == -1) return false;
for (int i = f; i < n - 1; i++) {
if (a[i] > a[i + 1]) {
end = i + 1;
}else break;
}
if (end == n - 1) return true;
else return false;
}
bool panduandown(const int a[]) {
int f = -1, end = 0;
for (int i = 0; i < n - 1; i++) {
if (a[i] > a[i + 1]) {
f = i + 1;
}else break;
}
if (f == -1) return false;
for (int i = f; i < n - 1; i++) {
if (a[i] < a[i + 1]) {
end = i + 1;
}else break;
}
if (end == n - 1) return true;
else return false;
}
int main() {
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin>>n;
for(int i = 0; i < n; i++) {
cin>>a[i];
}
bool up_down = panduanup(a);
bool down_up = panduandown(a);
if (up_down || down_up) {
cout<<"YES\n";
}else {
cout<<"NO\n";
}
return 0;
}
二、K题
找规律 + 快速幂
需要用到一些高数知识。
1.基本关系式为: a n = 2 a n − 1 + n a_{n} = 2a_{n - 1} + n an=2an−1+n
2.求其通向公式
a n = 2 n + 1 − n − 2 a_n=2^{n + 1} - n - 2 an=2n+1−n−2
3.过程:构造 a n + n + 2 = 2 ( a n − 1 + n + 1 ) a_{n} + n + 2 = 2\left( a_{n - 1} + n + 1 \right) an+n+2=2(an−1+n+1),令 c n = a n + n + 2 c_{n} = a_{n} + n + 2 cn=an+n+2,则 c n = 2 c n − 1 c_{n} = 2c_{n - 1} cn=2cn−1,首项 c 1 = a 1 + 1 + 2 = 1 + 3 = 4 c_1 = a_1 + 1 + 2 = 1 + 3 = 4 c1=a1+1+2=1+3=4。等比数列 { c n } \{ c_n \} {cn} 的通项为 c n = 4 ⋅ 2 n − 1 = 2 n + 1 c_{n} = 4 \cdot 2^{n - 1} = 2^{n + 1} cn=4⋅2n−1=2n+1,因此: a n = 2 n + 1 − n − 2 a_{n} = 2^{n + 1} - n - 2 an=2n+1−n−2
cpp
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod = 1e9+9;
int n;
long long qmi(long long a,long long b) {
long long res = 1;
while(b) {
if (b&1) {
res = res * a % mod;
}
a = a * a % mod;
b = b >> 1;
}
return res % mod;
}
signed main() {
ios_base::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin >> n;
int ans = ((qmi(2,n + 1) - n % mod - 2) % mod + mod) % mod;
cout << ans << '\n';
return 0;
}
三、C题
组合计数 + 模拟加法
需要用到__128类型(long long会卡)
乘以2的x次方对多少个1没有影响,可以看作左移x位(注意题目中Binary二进制)
cpp
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
long long n, m, k;
cin >> n >> m >> k;
if (n == m) {
cout << 1 << endl;
return 0;
}
long long d = abs(n - m);
if (d >= 100) {
vector<__int128> comb(k+1);
comb[0] = 1;
for (int i = 1; i <= k; i++) {
comb[i] = comb[i-1] * (k - i + 1) / i;
}
int ans = 0;
for (int i = 0; i <= k; i++) {
__int128 num = comb[i];
int cnt = 0;
while (num > 0) {
if (num & 1) cnt++;
num >>= 1;
}
ans += cnt;
}
cout << ans << endl;
} else {
int L = d * k + 1000;
vector<int> bits(L, 0);
bits[0] = 1;
for (int i = 0; i < k; i++) {
vector<int> new_bits(L, 0);
int carry = 0;
for (int j = 0; j < L; j++) {
int s = carry;
s += bits[j];
if (j >= d) {
s += bits[j - d];
}
new_bits[j] = s % 2;
carry = s / 2;
}
bits = new_bits;
}
int ans = 0;
for (int i = 0; i < L; i++) {
if (bits[i] == 1) {
ans++;
}
}
cout << ans << endl;
}
return 0;
}
四、L题
思维 + 交互设计
在瓶子中分别取1 2 3 ...个最后总和减去药片数量 * 500的数字就是残次品的个数即在哪个瓶子里。
cpp
#include <bits/stdc++.h>
using namespace std;
int N;
int main() {
scanf("%d",&N);
printf("%d\n",N);
long long pillnum = 0;
for(int i = 1; i <= N; i++) {
printf("%d %d\n",i,i);
pillnum += i;
}
fflush(stdout);
long long sum = 0;
scanf("%lld",&sum);
printf("! %lld\n",sum - pillnum * 500);
fflush(stdout);
return 0;
}
五、E题
注意这道题本来以为要用高精度所以想着python处理起来简单,用python写的,结果没用上。
本题目有小坑:不能直接模拟题目中的除法后加和,必须通分后化简最后加和。因为精度问题。作者给你们试过了wa
python
import math
#import
n, x = map(int, input().split())
a = list(map(int, input().split()))
a.sort(reverse=True)
sum_num = 0
sum_den = 1
for i in range(n):
pos = i + 1
ai = a[i]
up = ai
down = pos
new_up = sum_num * down + up * sum_den #小坑
new_down = sum_den * down
# 化简分数
g = math.gcd(new_up, new_down)
sum_num = new_up // g
sum_den = new_down // g
if sum_num >= x * sum_den:
print("YES")
exit()
print("NO")
六、H题
排序或运算符重载
没什么好说的,根据题目公式写就行了。
cpp
#include<bits/stdc++.h>
using namespace std;
typedef struct node {
string name;
int problems;
int place;
bool operator < (const node& n) const {
return this->place < n.place;
}
}node;
int doit(int rank) {
int ans = 0;
if (rank > 50) return 0;
else if (rank >= 11) ans += 50 - rank + 1;
else if (rank >= 6) ans += 40 + 2 * (10 - rank + 1);
else if (rank == 5) ans += 53;
else if (rank == 4) ans += 57;
else if (rank == 3) ans += 62;
else if (rank == 2) ans += 68;
else if (rank == 1) ans += 80;
return ans;
}
int cal(vector<node>& nodes) {
int num = nodes.size();
int ans = 0;
if (num >= 1) {
ans += 10 * nodes[0].problems;
int rank = nodes[0].place;
ans += 2 * doit(rank);
}
if (num >= 2) {
int rank = nodes[1].place;
ans += doit(rank);
}
if (num >= 3) {
int rank = nodes[2].place;
ans += doit(rank);
}
return ans;
}
int main() {
ios_base::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int n0;
cin>>n0;
vector<node> v;
for(int i=0;i<n0;i++) {
node temp;
cin>>temp.name>>temp.problems>>temp.place;
v.push_back(temp);
}
sort(v.begin(),v.end());
int n1;
cin>>n1;
vector<node> v1;
for(int i=0;i<n1;i++) {
node temp;
cin>>temp.name>>temp.problems>>temp.place;
v1.push_back(temp);
}
sort(v1.begin(),v1.end());
int n2;
cin>>n2;
vector<node> v2;
for(int i=0;i<n2;i++) {
node temp;
cin>>temp.name>>temp.problems>>temp.place;
v2.push_back(temp);
}
sort(v2.begin(),v2.end());
int num1 = cal(v);
int num2 = cal(v1);
int num3 = cal(v2);
int res = 4 * num1 + 3 * num2 + 2 * num3;
cout<<res<<'\n';
return 0;
}
七、N题
动态规划:dp[i]表示生成前i个字符的最小操作次数
状态转移(两种操作):
1.添加字符 :从长度 i 到 i+1,操作次数 +1 dp[i+1] = min(dp[i+1], dp[i] + 1)。
2.直接跳转:若 F[i] > i(存在更长的公共前缀),则从 i 直接跳到 F[i],操作次数 +1 dp[F[i]] = min(dp[F[i]], dp[i] + 1)。
cpp
#include <bits/stdc++.h>
using namespace std;
int main() {
int n;
cin >> n;
vector<string> files(n);
for (int i = 0; i < n; i++) {
cin >> files[i];
}
string target = files[0];
int L = target.size();
vector<int> L_arr;
for (int i = 0; i < n; i++) {
int common = 0;
for (int j = 0; j < files[i].size() && j < L; j++) {
if (files[i][j] == target[j]) {
common++;
} else {
break;
}
}
L_arr.push_back(common);
}
sort(L_arr.begin(), L_arr.end());
vector<int> F(L+1);
for (int i = 0; i <= L; i++) {
auto it = lower_bound(L_arr.begin(), L_arr.end(), i);
if (it != L_arr.end()) {
F[i] = *it;
} else {
F[i] = L;
}
}
vector<int> dp(L+1, 1e9);
dp[0] = 0;
for (int i = 0; i <= L; i++) {
if (i < L) {
dp[i+1] = min(dp[i+1], dp[i] + 1);
}
if (F[i] > i && F[i] <= L) {
dp[F[i]] = min(dp[F[i]], dp[i] + 1);
}
}
cout << dp[L] << endl;
return 0;
}
八、i题
简单图论 + dfs
cpp
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
int h[N],e[2 * N],ne[2 * N],idx;
inline void add(int a,int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
int vis[N],du[N];
int headnum, midnum, tailnum, du3_1, du3_2, du_is_4;
inline void dfs(int t,int num,int f) {
if (t == du_is_4) f = 1;
if (t == du3_2 && num != 2) {
if (f) {midnum = num;}
else{headnum = num;}
return;
}
for(int i = h[t]; ~i; i = ne[i]) {
if (!vis[e[i]]) {
vis[e[i]] = 1;
dfs(e[i],num + 1,f);
vis[e[i]] = 0;
}
}
}
int main() {
ios_base::sync_with_stdio(false),cin.tie(0),cout.tie(0);
memset(h,-1,sizeof h);
int n;
cin>>n;
for (int i = 0; i < n + 2; i++) {
int a,b;
cin>>a>>b;
add(a,b);
add(b,a);
}
vector<int>du_is_3;
for (int i = 1; i <= n; i++) {
int tep = 0;
for (int j = h[i]; j != -1; j = ne[j]) {
tep ++;
}
du[i] = tep;
if (tep == 3) {
du_is_3.push_back(i);
}
if (tep == 4) {
du_is_4 = i;
}
}
du3_1 = du_is_3[0] , du3_2 = du_is_3[1];
vis[du3_1] = 1;
dfs(du3_1,1,0);
vis[du3_1] = 0;
tailnum = n + 3 - headnum - midnum;
cout<<headnum<<" "<<midnum<<" "<<tailnum<<'\n';
return 0;
}
九、J题
纯粹的暴力,数据量太小了,像滑动窗口但不是。
cpp
#include <bits/stdc++.h>
using namespace std;
int t[105],r[105];
int main() {
ios_base::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int n,k,c;
cin>>n>>k>>c;
for (int i = 0; i < c; i++) {
cin>>t[i]>>r[i];
// cout<<t[i]<<" "<<r[i]<<endl;
}
vector<vector<int>> a(105);
for (int i = 0; i < c; i++) {
for (int j = 1; j * t[i] <= n; j++) {
a[i].push_back(j * t[i]);
}
}
int l = 1;//1 k
int i;
for (i = l; i <= n - k + 1; i++) {
bool f = 0;
for (int j = 0; j < c; j++) {
int cnt = 0;
for (int p = 0; p < a[j].size(); p++) {
if (a[j][p] >= i && a[j][p] <= i + k - 1) {
cnt++;
}
}
if (cnt != r[j]) {
f = 1;
break;
}
}
if (!f) break;
}
if (i <= n - k + 1) {
cout<<i<<'\n';
}else {
cout<<-1<<'\n';
}
return 0;
}
十、G题
图论 + 启发式合并 + dfs
这个题故意卡的,需要用到启发式合并。思路很简单,作者之前真的没写过要启发式合并的题。这道题链式前向星建图就有些不太好用了,因为下面的dfs要以size大的节点开始到小。反而用这种建图方式好排序。
不用启发式合并会卡在test47。看的其他大佬的算法明白这样处理。长脑子了。
cpp
#include <bits/stdc++.h>
using namespace std;
typedef vector<int> vi;
typedef vector<vi> IntMatrix;
typedef pair<int, int> pii;
typedef vector<pii> PairVector;
typedef vector<PairVector> PairMatrix;
int n;
PairMatrix adjlist;
vi nodeColors;
vi ans;
int cal_size(int node) {
int size = 1;
for (auto& [treesize, childnode] : adjlist[node]) {
treesize = cal_size(childnode);
size += treesize;
}
sort(adjlist[node].rbegin(), adjlist[node].rend());
return size;
}
unordered_map<int, int> dfs(int node) {
if (adjlist[node].empty()) {
ans[node] = nodeColors[node];
return {{nodeColors[node], 1}};
}
unordered_map<int, int> colorCounts = dfs(adjlist[node][0].second);
int mostcolor = ans[adjlist[node][0].second];
for (int i = 1; i < adjlist[node].size(); i++) {
int childNode = adjlist[node][i].second;
auto childColorCounts = dfs(childNode);
for (auto [color, count] : childColorCounts) {
colorCounts[color] += count;
if (colorCounts[color] > colorCounts[mostcolor] ||
(colorCounts[color] == colorCounts[mostcolor] && color < mostcolor)) {
mostcolor = color;
}
}
}
int currentColor = nodeColors[node];
colorCounts[currentColor]++;
if (colorCounts[currentColor] > colorCounts[mostcolor] ||
(colorCounts[currentColor] == colorCounts[mostcolor] && currentColor < mostcolor)) {
mostcolor = currentColor;
}
ans[node] = mostcolor;
return colorCounts;
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n;
adjlist.assign(n + 1, {});
ans.assign(n + 1, 0);
nodeColors.assign(n + 1, 0);
vi inDegree(n + 1, 0);
for (int i = 1; i <= n; i++) {
cin >> nodeColors[i];
}
for (int i = 0; i < n - 1; i++) {
int u, v;
cin >> u >> v;
adjlist[u].emplace_back(0, v);
inDegree[v]++;
}
int root = 1;
for (int i = 1; i <= n; i++) {
if (inDegree[i] == 0) {
root = i;
break;
}
}
cal_size(root);
dfs(root);
for (int i = 1; i <= n; i++) {
cout << ans[i] << ' ';
}
return 0;
}