2022.4.28~2022.5.3的CF刷题总结【标签: constructive algorithms 难度: 1300~1500】
作者:互联网
4.28
D1. Mocha and Diana (Easy Version)
Problem - 1559D1 - Codeforces 难度:1400,类型:连通图,暴力,并查集
题意
已知两个无向图(简称图一和图二),都有n个点,图一有m1条边,图二有m2条边。请问可以最多共同加多少边使得两个图变成无向无环图
分析
鉴于n取值为1~1e3,可以考虑暴力二重循环暴力(1~n)*(1~n)看是否符合条件进行加边。
且必有一个图能连n-1个边。
可以优化的地方
对于find1和find2函数,可以合并成一个,形参和实参里加一个数组,简易部分代码如下
int find(int x, int* p) { if(p[x]!=x) p[x] = find1(p[x]); return p[x]; } find(i,p1);//找第一个图(p1)的i父节点 find(i,p2);//找第二个图(p2)的i父节点View Code
AC代码
#include <bits/stdc++.h> using namespace std; typedef long long LL; const int N = 1010; int p1[N], p2[N]; int find1(int x) { if(p1[x]!=x) p1[x] = find1(p1[x]); return p1[x]; } int find2(int x) { if(p2[x]!=x) p2[x] = find2(p2[x]); return p2[x]; } int main() { int n, m1, m2; cin >> n >> m1 >>m2; for(int i = 1; i <= n; i ++) p1[i] = i, p2[i]=i; for(int i = 0; i < m1; i ++) { int a, b; cin >> a >>b; p1[find1(b)] = find1(a); } for(int i = 0; i < m2; i ++) { int a, b; cin >> a >>b; p2[find2(b)] = find2(a); } cout << n-max(m1,m2)-1<<endl; for(int i = 1; i <= n; i ++) find1(i), find2(i); for(int i = 1; i <= n; i ++) { for(int j = 1; j <= n; j ++) { int p11 = find1(i), p12 = find1(j);//提前找到父节点,顺便更新 int p21 = find2(i), p22 = find2(j); if(p11 != p12 && p21!=p22 ) { cout<<i<<' '<<j<<'\n'; p2[p21] = p22; p1[p11] = p12; } } } return 0; }View Code
4.29
D2. Mocha and Diana (Hard Version)
Problem - 1559D2 - Codeforces 难度:2500,类型:连通图,贪心,并查集
题意
已知两个无向图(简称图一和图二),都有n个点,图一有m1条边,图二有m2条边。请问可以最多共同加多少边使得两个图变成无向无环图,n的取值与上题相比改为(1~1e5)
分析
鉴于n取值为1~1e5,只能考虑在nlogn内解决,是看了题解后做出的,做法如下:
必有一个图能连n-1个边。
经过简单版的做题, 我们发现输出中前面很多都有1这个点,所以这里我们进行优化,简单分为 “与1相连的” 和 “不与1相连的”。
1. 两图都与1不相连的点:都连到数字1上,直接输出 "i 1"
2. 两图中只有一个与1相连的点:图一没有与1相连进入队列q1, 图二没有与1相连进入队列q2。一共输出min(q1.size(),q2.size())次,每次输出q1和q2队首 "q1.top() q2.top()" ,并弹出队首
解释下第二个步骤的原因: 做完步骤一后,剩下的点就是 在图一与1相连但是在图二不与1相连的点 和 在图一不与1相连但是在图二与1相连的点,这种点如何连到带有1的连通块呢?
这种点属于不能与1直接相连,但是可以间接与1相连,q1.top() q2.top()就非常合适,q1.top在图一不与1相连,在图二与1相连; q2.top在图一与1相连,在图二不与1相连,二者互补
实在不懂可以画图理解下题目例2
AC代码
/* D1. Mocha and Diana (Easy Version) https://codeforces.com/problemset/problem/1559/D1 */ #include <bits/stdc++.h> using namespace std; typedef long long LL; const int N = 1e5+10; int p1[N], p2[N]; int find1(int x) { if(p1[x]!=x) p1[x] = find1(p1[x]); return p1[x]; } int find2(int x) { if(p2[x]!=x) p2[x] = find2(p2[x]); return p2[x]; } int main() { int n, m1, m2; cin >> n >> m1 >>m2; for(int i = 1; i <= n; i ++) p1[i] = i, p2[i]=i; for(int i = 0; i < m1; i ++) { int a, b; cin >> a >>b; p1[find1(b)] = find1(a); } for(int i = 0; i < m2; i ++) { int a, b; cin >> a >>b; p2[find2(b)] = find2(a); } cout << n-max(m1,m2)-1<<endl; for(int i = 1; i <= n; i ++) find1(i), find2(i); for(int i = 2; i <= n; i ++) { int p11 = find1(i), p12 = find1(1); int p21 = find2(i), p22 = find2(1); if(p11 != p12 && p21!=p22 ) { cout<<i<<' '<<1<<'\n'; p2[p21] = p22; p1[p11] = p12; } } vector<int> q1,q2; for(int i = 2; i <= n; i ++) { if(p1[i] == i && p1[i] != p1[1]) q1.push_back(i); if(p2[i] == i && p2[i] != p2[1]) q2.push_back(i); } for(int i = 0; i < q1.size() && i<q2.size(); i ++) { cout << q1[i] << ' ' << q2[i]<<'\n'; } return 0; }View Code
C. Coin Rows
Problem - 1555C - Codeforces 难度:1300 , 类型:前缀和,动下脑
题意
小A和小B玩博弈游戏,2行m列图,每次可以向下或向右走,小A先走,且他想让小B的路径值和最小,小B想让自己路径和最大,走过的路该点值清零
分析
画图再看,你会发现小A剩下的路是这个样子滴(白色的),i 的取值范围是 1~m
AC代码
#include<bits/stdc++.h> using namespace std; const int N = 1e5 + 10; int a[2][N]; int main() { int t; cin>>t; while(t--) { int n; cin>> n; for(int i = 1; i <= n; i ++) cin >> a[0][i], a[0][i] += a[0][i-1]; for(int i = 1; i <= n; i ++) cin >> a[1][i], a[1][i] += a[1][i-1]; int res= 999999999; for(int i = 1; i <= n; i ++) { int x1 = a[0][n]-a[0][i]; int x2 = a[1][i-1]; res = min(res, max(x1,x2)); } cout <<res<<endl; } return 0; }View Code
4.30
B2. Wonderful Coloring - 2
【题很好,值得再做】
Problem - 1551B2 - Codeforces 难度:1400 类型:涂色模拟
题解: B2. Wonderful Coloring - 2 【模拟+简洁优化】 - la-la-wanf - 博客园 (cnblogs.com)
5.1
B. Plus and Multiply【很难】【记得开long long】
Problem - 1542B - Codeforces 难度:1500,类型:数学加乘,需动手画
题意
给出 n, a, b 。初始值是1,n是目的值,每次可以乘 a,每次也可以加b,问是否可以得到 n
分析
这个题就是挺巧的,让我再做也不一定做出来,可以看一下运算的式子然后化简
每一个加号都会让化简的式子多几项,每一项就是 b*(无数个a相乘), 但是有一项是1*(a^i),把(1*a^i)单独抽出来,式子就成了b*(a^i1 + a^i2 +......+k), k, i 都是任意非负数
也就是说
, z和ki是任意非负数
做法: n - az能被b整除则为yes
AC代码
#include<bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<int,int> PII; const int N = 2e5 + 10; int a,b; int main() { int t; cin>>t; while(t--) { bool f=0; int n; cin >> n >> a >> b; if(a==1) { n--; if(n%b==0)puts("YES"); else puts("NO"); continue; } LL aa = 1;//开longlong才不会超时 while(aa<=n) { if((n-aa)%b==0 || n==aa) { f=1; break; } aa*=a; }//di if(f)puts("YES"); else puts("NO"); } return 0; }View Code
A. Great Graphs 【开long long】
题意
n个点,给出每个点到下一个点的值(如果是到前面去就是该值的负数),问如何搭建才能使边权值之和最小。
分析
权值和最小肯定要去构造负数边,先小到大排个序,每个点肯定要去后面所有地方 res += -b[i] *(i-1) + b[i] * (n-i);, 这里要开long long
AC代码
#include<bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<int,int> PII; const int N = 1e5 + 10; LL b[N]; int main() { int t; cin>>t; while(t--) { int n; cin >> n; LL res = 0; for(int i = 1; i <= n; i ++) cin >> b[i]; sort(b+1, b+n+1); for(int i = 2; i <= n; i ++) res+= b[i]-b[i-1]; for (int i = 1; i <= n; i ++) { res += -b[i] *(LL)(i-1) + b[i] * (LL)(n-i); } cout << res<<endl; } return 0; }View Code
5.2
D. Co-growing Sequence 简单题
Problem - 1547D - Codeforces 难度1300, 类型:简单构造
题意
给出序列a, 要求求出序列b,使得 设xi = ai | bi 满足 xi & xi+1=xi
分析
挨个枚举就好了 O(n*30)
C. Fillomino 2 简单题
Problem - 1517C - Codeforces 难度:1400,类型:简单深搜
题意
第 i 行一共有 i 个数, 每一行最后一个数 pi 已给出,要求 pi 要有pi个pi相连,请输出构造后的序列
分析
从最后往前遍历,每个数先往下走在往左走,dfs一遍就好
简单贴下代码
#include<bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<int,int> PII; const int N = 510; int a[N][N], n; int tx[2]={1,0}, ty[2]={0,-1}; bool dfs(int x,int y, int j,int num) { if(num==j) return 1; for(int i = 0; i <= 1; i ++) { int dx = x+tx[i], dy = y+ty[i]; if(dx<=0||dy<=0||dx>n||dy>=n || dy>dx) continue; if(a[dx][dy]!=0)continue; a[dx][dy] = j; if(dfs(dx,dy,j,num+1)) return 1; } } int main() { memset(a, 0, sizeof a); cin >> n; for(int i = 1; i <= n; i ++) cin >> a[i][i]; for(int i = n; i >= 1; i --) dfs(i, i, a[i][i], 1); for(int i = 1; i <= n; i ++) { for(int j = 1; j <= i; j ++) cout << a[i][j] <<' '; cout << '\n'; } return 0; }View Code
E. Restoring the Permutation
Problem - 1506E - Codeforces 难度 1500,类型:数字构造
题意
给出数组 qi=max(p1,p2,…,pi) ,要求出原数组pi的最小序列和最大序列,最小序列就是字典序最小
分析
无论最小还是最大序列,qi第一次出现的数,pi一定也是这个数
最小序列好说,除去固定值,从1开始看谁没被用过就行了
最大序列难搞些,前面的数要尽量大,就从下标1开始遍历,遇到固定值就从固定值-1开始遍历,如果用过就跳过,注意:这样可能会把几段序列遍历多次,比如100001 100001 100001...... 100002,所以下面代码采取了ne[i]数组表示 i~ne[i]-1的数都已经走过了,从ne[i]开始走即可
AC代码
#include<bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<int,int> PII; const int N = 2e5+10; int a[N]; int maxx[N], minn[N], ne[N]; bool st1[N], st2[N]; int main() { int t; scanf("%d", &t); while(t --) { int n; scanf("%d", &n); for(int i = 1; i <= n; i ++) { scanf("%d", &a[i]); minn[i] = 0; maxx[i]=0; st1[i]=st2[i]=0; ne[i]=i-1; } for(int i = 1; i <= n; i ++) if(a[i] != a[i-1]) maxx[i] = minn[i] = a[i], st1[a[i]]=1, st2[a[i]]=1; int l = 1; for(int i = 2; i <= n; i ++)//minn { while(st1[l]) ++l; if(minn[i]==0) minn[i] = l++; } int r =a[1]; for(int i = 2; i <=n; i++) { if(maxx[i]==0) { int r1 = r; while(st2[r]) r=ne[r];//巧妙了用了链表的思想,要不然会超时 ne[r1]=r-1; st2[r]=1; maxx[i] = r--; } else r=maxx[i]-1; } for(int i = 1; i <= n;i ++) printf("%d ", minn[i]); printf("\n"); for(int i = 1; i <= n;i ++) printf("%d ", maxx[i]); printf("\n"); } return 0; }View Code
D. Epic Transformation
Problem - 1506D - Codeforces 难度:1400, 类型:数字简单构造,贪心
题意
每次可以删除两个不同的数,问最后剩几个数
分析
统计出现次数最多的数与n比较即可
AC代码
#include<bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<int,int> PII; const int N = 2e5+10; int a; int main() { int t; scanf("%d", &t); while(t --) { int n; scanf("%d", &n); map<int,int> mp; int maxx = 0; for(int i = 1; i <= n; i ++) { scanf("%d", &a); mp[a]++; maxx = max(maxx, mp[a]); } int res = 0; if(n%2) res++, maxx--, n--; res+=max(0,maxx*2-n); printf("%d\n", res); } return 0; }View Code
标签:p2,typedef,p1,1300,int,28,CF,long,cin 来源: https://www.cnblogs.com/la-la-wanf/p/16219481.html