Codeforces Round #506 (Div. 3) 解题报告

版权声明:转载请声明出处,谢谢支持 https://blog.csdn.net/Dreamstar_DS/article/details/82152136

或许是第一场非校内比赛???


第一次打比赛,然而并没有发挥好,连Div3不能锁题这种事情都要跑到群上去问…A了三题,本来听打过的同学说是要掉rating了,结果涨了一些2333,可能因为我是1500吧

比赛链接:http://codeforces.com/contest/1029

题面粘贴格式不大好又不大会弄,请自行前往比赛页面查看吧

A. Many Equal Substrings

A题简单说下,给你一个字符串,求包含k个此字符串的字符串的最小长度,实际上我们只需要求最长的前缀和后缀相同的部分,然后再补这个字符串时就不用补这部分了
AC Code:

#include<bits/stdc++.h>
#define rg register
#define il inline
#define ll long long
#define maxn 500005
using namespace std;
il int read(){rg int x = 0 , w = 1;rg char ch = getchar();while (ch < '0' || ch > '9'){if (ch == '-') w = -1;ch = getchar();}while (ch >= '0' && ch <= '9'){x = (x<<3) + (x<<1) +ch - '0';ch =getchar();}return x * w;}
char x[maxn] , y[maxn];
int main(){
    rg int n = read() , t = read();
    scanf("%s",x);
    for (rg int i = n - 1;i >= 1;--i){
        bool ok = 1;
        for (rg int j = 0;j < i;++j){
            if (x[j] != x[n - i + j]) {ok = 0;break;}
        }
        if (ok == 1){
            rg int cnt = n;
            for (rg int tt = 1;tt < t;++tt)
                for (rg int j = i;j <= n - 1;++j){
                    x[cnt++] = x[j];
                }
            for (rg int j = 0;j < cnt;++j) putchar(x[j]);
            return 0;
        }
    }
    for (rg int i = 1;i <= t;++i) printf("%s",x);
    return 0;
}

B. Creating the Contest

B题也挺简单的,大意是求最长上升子序列并且后一项不能超过前一项的2倍,本来应该是个DP,但是输入数据保证升序,我还能说什么吗…
AC Code:

#include<bits/stdc++.h>
#define rg register
#define il inline
#define ll long long
#define maxn 500005
using namespace std;
il int read(){rg int x = 0 , w = 1;rg char ch = getchar();while (ch < '0' || ch > '9'){if (ch == '-') w = -1;ch = getchar();}while (ch >= '0' && ch <= '9'){x = (x<<3) + (x<<1) +ch - '0';ch =getchar();}return x * w;}
int a[maxn];
int main(){
    rg int n = read();
    int maxl = 1 , cnt = 1;
    for (rg int i = 1;i <= n;++i) a[i] = read();
    for (rg int i = 2;i <= n;++i){
        if (a[i] > (a[i - 1] << 1)) cnt = 0;
        ++cnt;
        maxl = max(maxl , cnt);
    }
    cout<<maxl;
    return 0;
}

C. Maximal Intersection

C题乍看可以秒A,我的理解是去掉最短区间就可以了,事实上我并没有考虑一个比最短区间较长的区间不与其它任何一个区间有交集的情况,这时答案自然为0了,那我们回归我们想求的答案,即length,题目中定义的是右端点减去左端点,即所有区间的Minr - Maxl , 因为只能改变一个区间,所以只能改变Minr 或 Maxl 或both(这种情况被包含在前两种情况中),我们两种情况取个MAX就好了

#include<bits/stdc++.h>
#define rg register
#define il inline
#define ll long long
#define maxn 500005
using namespace std;
il int read(){rg int x = 0 , w = 1;rg char ch = getchar();while (ch < '0' || ch > '9'){if (ch == '-') w = -1;ch = getchar();}while (ch >= '0' && ch <= '9'){x = (x<<3) + (x<<1) +ch - '0';ch =getchar();}return x * w;}
struct node{
    int l , r ,id;  
}a[maxn];
int main(){
    rg int n = read() , minl = 1000000005;
    int k;
    int maxl = -1 , minr = 1000000005 ,k1 ,k2;
    for (rg int i = 1;i <= n;++i){
        a[i].l = read() , a[i].r = read() , a[i].id = i;
        if (a[i].l > maxl) maxl = a[i].l , k1 = i;
        if (a[i].r < minr) minr = a[i].r , k2 = i;
    }
    k = k1;
    int ans = 0;
    maxl = -1 , minr = 1000000005;
    for (rg int i = 1;i <= n;++i){
        if (i != k) maxl = max(maxl , a[i].l) , minr = min(minr , a[i].r);
    }
    ans = max(ans , minr - maxl);
    k = k2;
    maxl = -1 , minr = 1000000005;
        for (rg int i = 1;i <= n;++i){
        if (i != k) maxl = max(maxl , a[i].l) , minr = min(minr , a[i].r);
    }
    ans = max(ans , minr - maxl);
    cout<<ans;
    return 0;
}

D. Concatenated Multiples

赛场上没想出来2333
大意是,给你一个长为n序列和一个数k,求有多少对数拼接起来是k的倍数。(拼接即为类似于字符串的拼接,如45拼接1为451)
n方显然不能过,于是我们希望预处理一些有用的东西,来优化我们的运算,这里我们可以将与其他数拼接的这个过程优化到O1,由于数字最大只能到10^9,我们将每个数左移k(1 <= k <= 10)位的情况枚举出来,mp[i][j]表示左移i位时余数为j的数字的个数,因此与其它数拼接我们直接+=mp[len][(k - 原数%k余数) % k]即可,求该数长度len是个log10(10^9)的过程(我没用log10当然大家完全可以用上优化一下),可以通过
AC Code:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<map>
#define rg register
#define il inline
#define maxn 500005
#define ll long long
using namespace std;
ll a[maxn] , b[maxn];
map <ll , ll> mp[12];
il ll read(){rg int x = 0 ,w = 1;char ch = getchar();while (ch < '0' || ch > '9'){if (ch == '-') w = -1;ch = getchar();}while (ch >= '0' && ch <= '9'){x = (x<<3) + (x<<1) + ch - '0';ch = getchar();}return x * w;}
int main(){
    ll n = read() , k = read();
    for (rg int i = 1 ; i <= n ; ++i) a[i] = read();
    for (rg int i = 1 ; i <= n ; ++i){
        ll tmp = a[i];
        for (rg int j = 1 ; j <= 10 ; ++j){
            tmp *= 10;
            tmp %= k;
            mp[j][tmp]++;
        }
    }
    ll ans = 0;
    for (int i = 1 ; i <= n ; ++i){
        ll len = 0 , tmp = a[i];
        while (tmp > 0) ++len , tmp /= 10;
        tmp = a[i] % k;
        ans += mp[len][(k - tmp) % k];
        ll x = 1;
        for (rg int j = 1;j <= len;++j) x = (x * 10) % k;
        if (!((((a[i] % k) * x) % k + (a[i] % k)) % k)) ans--;
    }
    cout << ans;
    return 0;   
}

E. Tree with Small Distances

题目大意:给你一棵树,你可以从1号节点到其他节点连一条边,求让1号节点到其它所有节点的距离<=2的最少连边数(边权都为1)
赛场上确实想用贪心做,但是以为要讨论几个>2节点的不同形态来分别贪心,觉得麻烦就放了,实际上我们对于每个待处理节点,可以发现向它的父亲连接边一定是最优的!于是本题这样就可以过了,删除节点我们直接用flag标记一下就行,由于不需要添加东西,我们sort一遍再处理就好了

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<map>
#define rg register
#define il inline
#define maxn 500005
#define ll long long
using namespace std;
int a[maxn] , b[maxn] , dis[maxn] , head[maxn] , cnt , faz[maxn] , tot , q[maxn];
bool flag[maxn];
il int read(){rg int x = 0 ,w = 1;char ch = getchar();while (ch < '0' || ch > '9'){if (ch == '-') w = -1;ch = getchar();}while (ch >= '0' && ch <= '9'){x = (x<<3) + (x<<1) + ch - '0';ch = getchar();}return x * w;}
struct edge{
    int to , next;  
}e[maxn << 1];
bool cmp(int a,int b){
    return dis[a] > dis[b];
}
void add(int u , int v){
    e[++cnt].to = v;
    e[cnt].next = head[u];
    head[u] = cnt;  
}
void dfs(int u ,int fa){
    faz[u] = fa;    
    for (rg int i = head[u] ; i ; i = e[i].next){
        rg int to = e[i].to;
        if (to != fa){
            dis[to] = dis[u] + 1;
            dfs(to , u);
            if (dis[to] > 2) q[++tot] = to;
            else flag[to] = 1;
        }
    }
}
int main(){
    rg int n = read() , u , v;
    for (rg int i = 1 ; i < n ; ++i) {u = read() , v = read();add(u , v);add(v , u);} 
    dfs(1 , 0);
    sort(q + 1 , q + 1 + tot , cmp);
    rg int l = 1 , r = tot;
    flag[1] = 1;
    ll res = 0;
    while (l <= r){
        if (flag[q[l]]) {++l ; continue;}
        ++res;
        rg int x = faz[q[l]];
        flag[x] = 1;
        for (rg int i = head[x] ; i ; i = e[i].next) flag[e[i].to] = 1;
    }
    cout << res;
    return 0;   
}

聪明的你,为何不思考一下当距离<=k时怎么做呢?

F. Multicolored Markers

题目大意:给你a个红色的方块和b个蓝色的方块,要求将它们拼成一个矩形,并且至少有一种颜色的方块能构成一个矩形,且让大矩形的周长最短

当时做题直接跳过了,没想到是暴力枚举就A…,话说赛场上怎么会去想枚举这种做法呢(CF不给部分分…)
a、b都有10^14这么大(话说外国题很喜欢开long long啊),大家一看数据其实就已经知道了,我们只需要枚举到sqrt(a + b)即可,我们在i循环时push进a、b的约数,保证矩形的宽(这里提出只是为了和长相区别)<= 大矩形的宽,每次提出最小的约数,(这能保证矩形尽量地紧凑,可以取到MIN值情况),我们用优先队列维护即可,再判断小矩形的长<=大矩形的长的话这就是一个合法图形,我们可以取它周长的MIN了

注意:此题的a、b很大,所以ans初始化时我们选用最坏情况初始化,不去无脑99999….

AC Code:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
#include<set>
#include<queue>
#define rg register
#define il inline
#define maxn 500005
#define ll long long
using namespace std;
priority_queue<ll , vector<ll> , greater<ll> >q;
int main(){
    ll a , b;
    cin >> a >> b;
    ll ans = (a + b + 1) << 1;//ans初始化
    for (rg ll i = 1;i * i <= a + b;++i){
        if (!(a % i)) q.push(a / i);//加入a、b的约数 
        if (!(b % i)) q.push(b / i);
        if (!((a + b) % i) && q.top() <= (a + b) / i){//如果i是a + b的约数,并且我们期望的小矩形的宽小于大矩形的宽 
            rg ll x = ((a + b) / i + i) << 1;//(接上)当然这里我们将a、b形成矩形同时考虑到了 
            ans = (x < ans)?x:ans;//取MIN 
        }
    }
    cout << ans;
}

猜你喜欢

转载自blog.csdn.net/Dreamstar_DS/article/details/82152136