Codeforces 1311A/B/C/D/E 题解

首先说还有F
但是F是树状数组的- -好久没做了 等复习那块的时候再做

A

水题 分情况讨论一下就行 加奇数或者减去偶数 从一个数达到另一个数
代码

int main() {
    int t;
    scanf("%d",&t);
    while(t--){
        int a,b;
        scanf("%d%d",&a,&b);
        if(a==b){
            printf("0\n");
        }else if(a<b){
            if((b-a)&1){
                printf("1\n");
            }else printf("2\n");
        }else{//a>b
            if((a-b)%2==0){
                printf("1\n");
            }else{
                printf("2\n");
            }
        }
    }
	return 0;
}

B

B很多题解都是冒泡排序 大致思路是 正常冒泡排序 不过要记录两个之间能不能交换 如果出现逆序且不能交换的情况 就说明不行
而自己的思路是并查集:结构体存刚开始的数据和位置 然后可以交换的两个坐标(p和p+1)在同一个并查集里 然后sort一下 遍历所有点 看有没有排序前后的不在同一个并查集里的 如果有 就说明不行。
下面AC代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
#include<stack>
#include<algorithm>
#define ll long long
#define inf 0x3f3f3f3f
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define MID (t[k].l+t[k].r)>>1
#define cl(a,b) memset(a,b,sizeof(a))
#define dbg() printf("aaa\n")
using namespace std;
int n,m;
struct node{
    int num,id;
    bool operator<(const node &oth){
        return num<oth.num;
    }
}a[110];
int p[110];
int pre[110];
int find(int x){
    if(x==pre[x]) return x;
    return pre[x]=find(pre[x]);
}
void join(int a,int b){
    int fa=find(a),fb=find(b);
    if(fa<fb) pre[b]=fa;
    else pre[a]=fb;
    return;
}
int main() {
	int T;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++){
            pre[i]=i;
            scanf("%d",&a[i].num);
            a[i].id=i;
        }
        for(int i=1;i<=m;i++){
            scanf("%d",&p[i]);
            join(p[i],p[i]+1);
        }
        for(int i=1;i<=n;i++) pre[i]=find(i);
        sort(a+1,a+n+1);
        bool flag=true;
        for(int i=1;i<=n;i++){
            if(find(i)!=find(a[i].id)){
                flag=false;
                break;
            }
        }
        if(flag){
            printf("YES\n");
        }else printf("NO\n");
    }
	return 0;
}

C

给你一个字符串 再给你一串数
让你统计字符串中各个字符(都是小写字母)的个数 而其中那一串数a[i]的含义是
重复加上前a[i]个字符的数量
(记得最后加上整个字符串- -自己忘了
用dp[26][maxn] 记录每个字母在每个位置出现过的总次数 每有一个a[i] 就给26个字母加上dp[][a[i]]次
下面是AC代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
#include<stack>
#include<algorithm>
#define ll long long
#define inf 0x3f3f3f3f
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define MID (t[k].l+t[k].r)>>1
#define cl(a,b) memset(a,b,sizeof(a))
#define dbg() printf("aaa\n")
using namespace std;
const int maxn=2e5+10;
//处理出每个字母的前缀和
char s[maxn];
int p[maxn],cnt[26];
int dp[26][maxn];
int main() {
    int T;
    scanf("%d",&T);
    while(T--){
        int n,m;
        scanf("%d%d",&n,&m);
        rep(i,0,25){
            rep(j,0,n){
                dp[i][j]=0;
            }
        }
        scanf("%s",s+1);
        rep(i,1,m) scanf("%d",&p[i]);
        rep(i,1,n){
            dp[s[i]-'a'][i]++;
        }
        rep(i,0,25){
            cnt[i]=0;
            rep(j,2,n){
                dp[i][j]+=dp[i][j-1];
            }
        }
        rep(i,1,m){//对于按错的地方
            int pp=p[i];
            rep(j,0,25){
                cnt[j]+=dp[j][pp];
            }
        }
        rep(j,0,25) cnt[j]+=dp[j][n];//加上最后!
        rep(i,0,25){
            printf("%d",cnt[i]);
            putchar(i==25?'\n':' ');
        }
    }
	return 0;
}

D Three Integers

这个自己当时想半天啊想半天 还是没做出来
没想到就是枚举所有方法 因为数的范围是10000嘛 所以可以直接枚举
但是10000的三次方肯定爆 所以是有剪枝的枚举 i++ j+=i k+=j 这样的 就不会爆了
再记录下需要操作的次数 (用枚举的ijk 和abc作差求绝对值
最后要注意 枚举到10000是不行的 因为你考虑5001 10000 10000这组数值
因为他说给的初始值是小于10000的 没说结果也是 所以上面这组值的答案是
5001 10002 10002 操作四次
下面是AC代码

const int maxn=10000;
int main() {
	int t;
    scanf("%d",&t);
    while(t--){
        int a,b,c;
        int a2,b2,c2;
        int ans=inf;
        scanf("%d%d%d",&a,&b,&c);
        for(int i=1;i<=15000;i++){
            for(int j=i;j<=15000;j+=i){
                for(int k=j;k<=15000;k+=j){//防止小溢出
                    int tp=abs(i-a)+abs(j-b)+abs(k-c);
                    if(ans>tp){
                        ans=tp;
                        a2=i,b2=j,c2=k;
                    }
                }
            }
        }
        printf("%d\n",ans);
        printf("%d %d %d\n",a2,b2,c2);
    }
	return 0;
}

E题 这个题就是给你结点数 给你树的总深度 问你能不能构造出一棵这样深度的树
思路:首先判断能不能 首先可以判断 深度最大是单链 最小是完全二叉树
先判断范围是否合适

bool judge(int nn,int dd){
    maxx=nn*(nn-1)/2;
    minn=0;
    int p[15];
    p[0]=1;
    rep(i,1,14) p[i]=p[i-1]*2;//p是数量
    int t=1;
    nn--;
    while(nn>=p[t]){
        minn+=t*p[t];
        nn-=p[t];
        t++;
    }
    if(nn) minn+=t*nn;//这里注意!
    return dd>=minn&&dd<=maxx;
}

合适的话 假设原来是单链 然后不断从后面往前寻找合适的移动位置。
什么是合适的移动位置?就是能让原来的最大深度降到所给深度
这里我们用vector存储 因为vector能随时获得这个深度有多少个结点!这个很重要
因为某一层的结点上限 是和上一层有密切联系的!最多不超过上一层的两倍
每次从最深的往前探测 终止条件是 没有越界(深度j>=0,所以for里j>0)且不超过maxx-d 这个值 还要看看当前深度的孩子数量有没有满 有的话往下挂 找的是双亲 然后要放在双亲下面一个 下面是这一段的代码

			for(int i=n-1;i>=1;i--){//深度从最深开始 往前挂
                int j;//从原来深度(i)的双亲(i-1)开始
                for(j=i-1;i-1-j<maxx-d&&j>0;j--);//找到能到达的最远的点
                while(dep[j].size()*2<=dep[j+1].size()) j++;//放满的不行
                dep[j+1].pb(dep[i][0]);//放在当前深度更深的一个
                dep[i].pop_back();//原来的地方删除它
                maxx-=i-1-j;
                if(maxx==d) break;
            }

反正思路有点复杂感觉 然后还要构造
从下往上构造

			for(int i=n-1;i>=1;i--){//构造树!
                if(dep[i].size()==0) continue;
                int j=0;//上一级的
                for(int k=0;k<dep[i].size();k++){
                    pre[dep[i][k]]=dep[i-1][j];
                    if(k&1) j++;
                }
            }

为什么从下往上呢?因为每个孩子都需要双亲 但是双亲不一定需要孩子!
下面是AC代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
#include<stack>
#include<algorithm>
#define ll long long
#define inf 0x3f3f3f3f
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define sd(a) scanf("%d",&a)
#define sdd(a,b) scanf("%d%d",&a,&b)
#define pb push_back
#define MID (t[k].l+t[k].r)>>1
#define cl(a,b) memset(a,b,sizeof(a))
#define dbg() printf("aaa\n")
using namespace std;
const int maxn=1e4+10;
int n,d;
int maxx,minn;
int pre[maxn];
vector<int> dep[maxn];//记录深度为i的点都是哪些点
bool judge(int nn,int dd){
    maxx=nn*(nn-1)/2;
    minn=0;
    int p[15];
    p[0]=1;
    rep(i,1,14) p[i]=p[i-1]*2;//p是数量
    int t=1;
    nn--;
    while(nn>=p[t]){
        minn+=t*p[t];
        nn-=p[t];
        t++;
    }
    if(nn) minn+=t*nn;//这里注意!
    return dd>=minn&&dd<=maxx;
}
int main() {
	int t;
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&n,&d);
        rep(i,0,n) dep[i].clear();
        if(!judge(n,d)){
            printf("NO\n");
        }else{
            printf("YES\n");
            rep(i,0,n-1){
                dep[i].pb(i+1);//i深度为i-1
            }
            for(int i=n-1;i>=1;i--){//深度从最深开始 往前挂
                int j;//从当前深度的双亲开始
                for(j=i-1;i-1-j<maxx-d&&j>0;j--);
                while(dep[j].size()*2<=dep[j+1].size()) j++;
                dep[j+1].pb(dep[i][0]);
                dep[i].pop_back();
                maxx-=i-1-j;
                if(maxx==d) break;
            }
            for(int i=n-1;i>=1;i--){//构造树!
                if(dep[i].size()==0) continue;
                int j=0;//上一级的
                for(int k=0;k<dep[i].size();k++){
                    pre[dep[i][k]]=dep[i-1][j];
                    if(k&1) j++;
                }
            }
            for(int i=2;i<=n;i++) {
                printf("%d",pre[i]);
                putchar(i==n?'\n':' ');
            }
        }
    }
	return 0;
}
发布了120 篇原创文章 · 获赞 12 · 访问量 5278

猜你喜欢

转载自blog.csdn.net/weixin_43735161/article/details/104587744