ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

2022.4.28~2022.5.3的CF刷题总结【标签: constructive algorithms 难度: 1300~1500】

2022-05-04 12:00:58  阅读:179  来源: 互联网

标签:p2 typedef p1 1300 int 28 CF long cin


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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有