其他分享
首页 > 其他分享> > 2022.4.28~2022.5.3的CF刷题总结【标签: constructive algorithms 难度: 1300~1500】

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