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函数,可以合并成一个,形参和实参里加一个数组,简易部分代码如下
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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父节点
AC代码
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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;
}
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代码
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/*
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;
}
C. Coin Rows
Problem - 1555C - Codeforces 难度:1300 , 类型:前缀和,动下脑
题意
小A和小B玩博弈游戏,2行m列图,每次可以向下或向右走,小A先走,且他想让小B的路径值和最小,小B想让自己路径和最大,走过的路该点值清零
分析
画图再看,你会发现小A剩下的路是这个样子滴(白色的),i 的取值范围是 1~m
AC代码
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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;
}
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代码
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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;
}
A. Great Graphs 【开long long】
Problem - 1540A - Codeforces 难度:1400, 类型: 构造建路
题意
n个点,给出每个点到下一个点的值(如果是到前面去就是该值的负数),问如何搭建才能使边权值之和最小。
分析
权值和最小肯定要去构造负数边,先小到大排个序,每个点肯定要去后面所有地方 res += -b[i] *(i-1) + b[i] * (n-i);, 这里要开long long
AC代码
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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;
}
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一遍就好
简单贴下代码
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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;
}
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代码
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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; }
D. Epic Transformation
Problem - 1506D - Codeforces 难度:1400, 类型:数字简单构造,贪心
题意
每次可以删除两个不同的数,问最后剩几个数
分析
统计出现次数最多的数与n比较即可
AC代码
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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; }