Codeforces Round #535(div 3) 简要题解
Problem A. Two distinct points
[题解]
显然 , 当l1不等于r2时 , (l1 , r2)是一组解
否则 , (l1 , l2)是一组合法的解
时间复杂度 : O(1)
[代码]
#include<bits/stdc++.h> using namespace std; typedef long long ll; typedef long double ld; typedef unsigned long long ull; template <typename T> inline void chkmax(T &x,T y) { x = max(x,y); } template <typename T> inline void chkmin(T &x,T y) { x = min(x,y); } template <typename T> inline void read(T &x) { T f = 1; x = 0; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = (x << 3) + (x << 1) + c - '0'; x *= f; } int main() { int T; read(T); while (T--) { int l1 , r1 , l2 , r2; read(l1); read(r1); read(l2); read(r2); if (l1 != r2) cout<< l1 << ' ' << r2 << '\n'; else cout<< l1 << ' ' << l2 << '\n'; } return 0; }
Problem B. Divisors of Two Integers
[题解]
首先 , 给定序列中的最大数一定是x和y中的一个数
将该数的所有因子从序列中删除一次 , 剩余数中的最大数即为另一个数
时间复杂度 : O(M) (取M = 10 ^ 4)
[代码]
#include<bits/stdc++.h> using namespace std; #define MAXN 10010 typedef long long ll; typedef long double ld; typedef unsigned long long ull; int n; int d[MAXN] , cnt[MAXN]; template <typename T> inline void chkmax(T &x,T y) { x = max(x,y); } template <typename T> inline void chkmin(T &x,T y) { x = min(x,y); } template <typename T> inline void read(T &x) { T f = 1; x = 0; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = (x << 3) + (x << 1) + c - '0'; x *= f; } int main() { read(n); int fst = 0 , sec = 0; for (int i = 1; i <= n; i++) { read(d[i]); if (d[i] > fst) fst = d[i]; ++cnt[d[i]]; } for (int i = 1; i <= fst; i++) if (fst % i == 0) --cnt[i]; for (int i = (int)1e4; i >= 1; i--) { if (cnt[i]) { sec = i; break; } } cout<< fst << ' ' << sec << '\n'; return 0; }
Problem C. Nice Garland
[题解]
通过观察发现 , 答案一定是一个以"R" , "G" , "B"三个字符所形成的一个排列为循环节循环得到的字符串
枚举排列 , 计算最优解 , 即可
时间复杂度 : O(N)
[代码]
#include<bits/stdc++.h> using namespace std; const int MAXN = 2e5 + 10; const int inf = 1e9; typedef long long ll; typedef long double ld; typedef unsigned long long ull; int n; char s[MAXN] , t1[MAXN] , t2[MAXN] , t3[MAXN] , t4[MAXN] , t5[MAXN] , t6[MAXN]; template <typename T> inline void chkmax(T &x,T y) { x = max(x,y); } template <typename T> inline void chkmin(T &x,T y) { x = min(x,y); } template <typename T> inline void read(T &x) { T f = 1; x = 0; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = (x << 3) + (x << 1) + c - '0'; x *= f; } int main() { scanf("%d" , &n); scanf("%s" , s + 1); for (int i = 1; i <= n; i++) { if (i % 3 == 1) t1[i] = 'R'; if (i % 3 == 2) t1[i] = 'G'; if (i % 3 == 0) t1[i] = 'B'; } int stp = 0 , ans = 0 , mstp = inf; for (int i = 1; i <= n; i++) if (s[i] != t1[i]) ++stp; if (stp < mstp) { mstp = stp; ans = 1; } for (int i = 1; i <= n; i++) { if (i % 3 == 1) t2[i] = 'R'; if (i % 3 == 2) t2[i] = 'B'; if (i % 3 == 0) t2[i] = 'G'; } stp = 0; for (int i = 1; i <= n; i++) if (s[i] != t2[i]) ++stp; if (stp < mstp) { mstp = stp; ans = 2; } for (int i = 1; i <= n; i++) { if (i % 3 == 1) t3[i] = 'B'; if (i % 3 == 2) t3[i] = 'R'; if (i % 3 == 0) t3[i] = 'G'; } stp = 0; for (int i = 1; i <= n; i++) if (s[i] != t3[i]) ++stp; if (stp < mstp) { mstp = stp; ans = 3; } for (int i = 1; i <= n; i++) { if (i % 3 == 1) t4[i] = 'B'; if (i % 3 == 2) t4[i] = 'G'; if (i % 3 == 0) t4[i] = 'R'; } stp = 0; for (int i = 1; i <= n; i++) if (s[i] != t4[i]) ++stp; if (stp < mstp) { mstp = stp; ans = 4; } for (int i = 1; i <= n; i++) { if (i % 3 == 1) t5[i] = 'G'; if (i % 3 == 2) t5[i] = 'R'; if (i % 3 == 0) t5[i] = 'B'; } stp = 0; for (int i = 1; i <= n; i++) if (s[i] != t5[i]) ++stp; if (stp < mstp) { mstp = stp; ans = 5; } for (int i = 1; i <= n; i++) { if (i % 3 == 1) t6[i] = 'G'; if (i % 3 == 2) t6[i] = 'B'; if (i % 3 == 0) t6[i] = 'R'; } stp = 0; for (int i = 1; i <= n; i++) if (s[i] != t6[i]) ++stp; if (stp < mstp) { mstp = stp; ans = 6; } cout<< mstp << '\n'; if (ans == 1) { for (int i = 1; i <= n; i++) putchar(t1[i]); cout<< '\n'; } if (ans == 2) { for (int i = 1; i <= n; i++) putchar(t2[i]); cout<< '\n'; } if (ans == 3) { for (int i = 1; i <= n; i++) putchar(t3[i]); cout<< '\n'; } if (ans == 4) { for (int i = 1; i <= n; i++) putchar(t4[i]); cout<< '\n'; } if (ans == 5) { for (int i = 1; i <= n; i++) putchar(t5[i]); cout<< '\n'; } if (ans == 6) { for (int i = 1; i <= n; i++) putchar(t6[i]); cout<< '\n'; } return 0; }
Problem D. Diverse Garland
[题解]
简单贪心即可
时间复杂度 : O(N)
[代码]
#include<bits/stdc++.h> using namespace std; const int MAXN = 2e5 + 10; typedef long long ll; typedef long double ld; typedef unsigned long long ull; int n , cnt; char s[MAXN]; template <typename T> inline void chkmax(T &x,T y) { x = max(x,y); } template <typename T> inline void chkmin(T &x,T y) { x = min(x,y); } template <typename T> inline void read(T &x) { T f = 1; x = 0; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = (x << 3) + (x << 1) + c - '0'; x *= f; } int main() { scanf("%d" , &n); scanf("%s" , s + 1); map<char , int> mp; mp.clear(); for (int i = 2; i < n; i++) { if (s[i] != s[i - 1]) continue; mp.clear(); mp[s[i - 1]]++; mp[s[i + 1]]++; if (mp['R'] && mp['G']) { s[i] = 'B'; ++cnt; } else if (mp['R'] && mp['B']) { s[i] = 'G'; ++cnt; } else if (mp['B'] && mp['G']) { s[i] = 'R'; ++cnt; } else if (mp['R']) { s[i] = 'G'; ++cnt; } else if (mp['G']) { s[i] = 'B'; ++cnt; } else { s[i] = 'R'; ++cnt; } } if (s[n] == s[n - 1]) {
int i = n;
mp.clear();
++mp[s[n - 1]];
if (mp['R'])
{
s[i] = 'G';
++cnt;
} else if (mp['G'])
{
s[i] = 'B';
++cnt;
} else
{
s[i] = 'R';
++cnt;
}
}
cout<< cnt << '\n'; for (int i = 1; i <= n; i++) putchar(s[i]); printf("\n"); return 0; }
Problem E. Array and Segments
[题解]
Easy Version :
枚举序列中的最大值和最小值的出现位置 , 然后枚举每一条线段 , 贪心地选择所有覆盖了最小值出现位置的线段
时间复杂度 : O(N ^ 2M)
[代码]
#include<bits/stdc++.h> using namespace std; #define MAXN 310 typedef long long ll; typedef long double ld; typedef unsigned long long ull; int n , m , ans; int a[MAXN] , l[MAXN] , r[MAXN]; vector< int > fans; template <typename T> inline void chkmax(T &x,T y) { x = max(x,y); } template <typename T> inline void chkmin(T &x,T y) { x = min(x,y); } template <typename T> inline void read(T &x) { T f = 1; x = 0; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = (x << 3) + (x << 1) + c - '0'; x *= f; } inline void solve(int x , int y) { int tx = a[x] , ty = a[y]; vector< int > res; res.clear(); for (int i = 1; i <= m; i++) { if (l[i] <= x && r[i] >= x) continue; if (l[i] <= y && r[i] >= y) { res.push_back(i); --ty; } } if (tx - ty > ans) { ans = tx - ty; fans.clear(); for (unsigned i = 0; i < res.size(); i++) fans.push_back(res[i]); } } int main() { read(n); read(m); for (int i = 1; i <= n; i++) read(a[i]); for (int i = 1; i <= m; i++) { read(l[i]); read(r[i]); } for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { if (i != j) solve(i , j); } } cout<< ans << '\n'; cout<< (int)fans.size() << '\n'; for (unsigned i = 0; i < (int)fans.size(); i++) cout<< fans[i] << ' '; cout<< '\n'; return 0; }
Hard Version :
我们可以先选择所有线段
那么 , 问题就转化为 , 选择一些区间进行操作 , 使得[li , ri]每个数增加1 , 最大化序列中的最大值 - 最小值
考虑枚举最大值出现的位置 , 显然 , 我们可以贪心地选择所有覆盖了该位置的线段
那么我们就可以使用扫描线算法 :
从1-n按顺序枚举最大值出现的位置i , 考虑所有以i为左端点的线段 , 我们选择这些线段进行操作 , 然后考虑所有以i为右端点的线段 , 取消对这些线段的操作
维护一棵支持区间修改 , 询问区间最大 / 最小值的线段树即可
时间复杂度 : O(NMlogN)
[代码]
#include<bits/stdc++.h> using namespace std; const int MAXN = 2e5 + 10; #pragma GCC optimize(2) #define rint register int typedef long long ll; typedef long double ld; typedef unsigned long long ull; int n , m; int l[MAXN] , r[MAXN] , val[MAXN]; vector< int > s[MAXN] , e[MAXN]; bool tag[MAXN]; struct Segment_Tree { struct Node { int l , r , tag; pair<int , int> value; } a[MAXN << 2]; inline void build(int index , int l , int r) { a[index].l = l , a[index].r = r; a[index].tag = 0; if (l == r) { a[index].value = make_pair(val[l] , val[l]); return; } int mid = (l + r) >> 1; build(index << 1 , l , mid); build(index << 1 | 1 , mid + 1 , r); update(index); } inline void update(int index) { a[index].value.first = min(a[index << 1].value.first , a[index << 1 | 1].value.first); a[index].value.second = max(a[index << 1].value.second , a[index << 1 | 1].value.second); } inline void pushdown(int index) { a[index << 1].value.first += a[index].tag; a[index << 1 | 1].value.first += a[index].tag; a[index << 1].value.second += a[index].tag; a[index << 1 | 1].value.second += a[index].tag; a[index << 1].tag += a[index].tag; a[index << 1 | 1].tag += a[index].tag; a[index].tag = 0; } inline void modify(int index , int l , int r , int val) { if (a[index].l == l && a[index].r == r) { a[index].value.first += val; a[index].value.second += val; a[index].tag += val; return; } pushdown(index); int mid = (a[index].l + a[index].r) >> 1; if (mid >= r) modify(index << 1 , l , r , val); else if (mid + 1 <= l) modify(index << 1 | 1 , l , r , val); else { modify(index << 1 , l , mid , val); modify(index << 1 | 1 , mid + 1 , r , val); } update(index); } inline pair<int , int> query() { return a[1].value; } } SGT; template <typename T> inline void chkmax(T &x,T y) { x = max(x,y); } template <typename T> inline void chkmin(T &x,T y) { x = min(x,y); } template <typename T> inline void read(T &x) { T f = 1; x = 0; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = (x << 3) + (x << 1) + c - '0'; x *= f; } int main() { read(n); read(m); for (rint i = 1; i <= n; i++) read(val[i]); SGT.build(1 , 1 , n); for (rint i = 1; i <= m; i++) { read(l[i]); read(r[i]); s[l[i]].push_back(r[i]); e[r[i]].push_back(l[i]); SGT.modify(1 , l[i] , r[i] , -1); } int mx = 0 , loc = 0; for (rint i = 1; i <= n; i++) { for (unsigned j = 0; j < s[i].size(); j++) SGT.modify(1 , i , s[i][j] , 1); pair<int , int> tmp = SGT.query(); if (tmp.second - tmp.first > mx) { mx = tmp.second - tmp.first; loc = i; } for (unsigned j = 0; j < e[i].size(); j++) SGT.modify(1 , e[i][j] , i , -1); } cout<< mx << '\n'; vector< int > res; for (int i = 1; i <= m; i++) { if (l[i] > loc || r[i] < loc) res.push_back(i); } cout<< (int)res.size() << '\n'; for (unsigned i = 0; i < res.size(); i++) cout<< res[i] << ' '; cout<< '\n'; return 0; }
Problem F. MST Unification
[题解]
首先用kruskal算法求出这个图的任意一棵最小生成树
枚举不在这颗最小生成树上的每一条边(u , v , w)
若加入这条边 , 则形成了一个环 , 若环上的边权除这条边外的最大值 = w , 那么说明可以用这条边替换环上权值 = w的边 , 我们需要将这条边的权值加一
倍增即可
时间复杂度 : O((N + M)logN)
[代码]
#include<bits/stdc++.h> using namespace std; typedef long long ll; typedef long double ld; typedef unsigned long long ull; const int MAXN = 2e5 + 10; const int MAXLOG = 20; struct Edge { int x,y; long long w; } edge[MAXN << 1]; int T,n,m,i; long long val; vector< pair<int,long long> > e[MAXN]; bool on_mst[MAXN]; int fa[MAXN],anc[MAXN][MAXLOG],dep[MAXN]; long long mx[MAXN][MAXLOG]; bool not_unique; inline bool cmp(Edge a,Edge b) { return a.w < b.w; } inline int get_root(int x) { if (fa[x] == x) return x; return fa[x] = get_root(fa[x]); } inline void kruskal() { int i,x,y,sx,sy; long long w; for (i = 1; i <= n; i++) fa[i] = i; for (i = 1; i <= m; i++) on_mst[i] = false; sort(edge+1,edge+m+1,cmp); for (i = 1; i <= m; i++) { x = edge[i].x; y = edge[i].y; w = edge[i].w; sx = get_root(x); sy = get_root(y); if (sx != sy) { on_mst[i] = true; val += w; fa[sx] = sy; e[x].push_back(make_pair(y,w)); e[y].push_back(make_pair(x,w)); } } } inline void build(int u) { int i,v; for (i = 1; i < MAXLOG; i++) { anc[u][i] = anc[anc[u][i-1]][i-1]; mx[u][i] = max(mx[u][i-1],mx[anc[u][i-1]][i-1]); } for (i = 0; i < e[u].size(); i++) { v = e[u][i].first; if (anc[u][0] != v) { dep[v] = dep[u] + 1; anc[v][0] = u; mx[v][0] = e[u][i].second; build(v); } } } inline long long get(int x,int y) { int i,t; long long ans = 0; if (dep[x] > dep[y]) swap(x,y); t = dep[y] - dep[x]; for (i = 0; i < MAXLOG; i++) { if (t & (1 << i)) { ans = max(ans,mx[y][i]); y = anc[y][i]; } } if (x == y) return ans; for (i = MAXLOG - 1; i >= 0; i--) { if (anc[x][i] != anc[y][i]) { ans = max(ans,max(mx[x][i],mx[y][i])); x = anc[x][i]; y = anc[y][i]; } } return max(ans,max(mx[x][0],mx[y][0])); } int main() { scanf("%d%d",&n,&m); val = 0; not_unique = false; for (i = 1; i <= n; i++) { dep[i] = 0; e[i].clear(); memset(anc[i],0,sizeof(anc[i])); memset(mx[i],0,sizeof(mx[i])); } for (i = 1; i <= m; i++) scanf("%d%d%lld",&edge[i].x,&edge[i].y,&edge[i].w); kruskal(); build(1); int ans = 0; for (i = 1; i <= m; i++) { if (!on_mst[i]) ans += (get(edge[i].x,edge[i].y) == edge[i].w); } cout<< ans << '\n'; return 0; }