codeforces 722系列ABCD 【套题】模拟+并查集+STL+数学二分

这套题其实应该是个上分场的,然后第一次锁了之后被HACK了,然后A就错了,自己也觉得挺搞笑的

但是最终是个只出了B题的小垃圾


A:12小时和24小时的标准作息时间,12小时制的小时时间是1-12,24小时的是0-23,分钟都是0-59

现在给你一个时间,要求你改动最少的数字,让它变得合理

被hack的数据是:

12

20:00

我当时是没有单独考虑,不为0,而且被10整除的情况,我是直接对10取模的,所以就傻逼错了

其实,就几类要改的情况:

当12小时制的时候,小时:0改成1,大于12的,如果被10整除改成10,否则,对10取模

当24小时制的时候,小时:0-23不变,其余的,对10取模

分钟与小时制无关,0-59不变,其余的对10取模

#include<bits/stdc++.h>
using namespace std;

int n,x,y;

int main(){
    //freopen("input.txt","r",stdin);
    while(scanf("%d",&n)!=EOF){
        scanf("%d:%d",&x,&y);
        if (n==12){
            if (x==0) x=1;
            else if (x>12){
                if (x%10!=0) x=x%10;
                else x=10;
            }
        }
        else{
            if (x>23) x=x%10;
        }
        if (y>59) y=y%10;
        printf("%02d:%02d\n",x,y);
    }
    return 0;
}

B:题目中废话挺多的,其实就是数一数有几个“元音”字母:aeiouy

注意有一个Hack点,在比较的时候,只能用不等号来作为判断依据

代码:

#include<bits/stdc++.h>
using namespace std;

const int maxn=150;
const int maxl=150;

int n;
int p[maxn],a[maxn];
char s[maxl];

int main(){
    //freopen("input.txt","r",stdin);
    while(scanf("%d",&n)!=EOF){
        for(int i=1;i<=n;i++) scanf("%d",&p[i]);
        memset(a,0,sizeof(a));
        bool flag=true;
        getchar();
        for(int i=1;i<=n;i++){
            gets(s);
            int len=strlen(s);
            for(int j=0;j<len;j++)
                if (s[j]=='a'||s[j]=='e'||s[j]=='i'||s[j]=='o'||s[j]=='u'||s[j]=='y') a[i]++;
            if (a[i]!=p[i]) flag=false;
        }
        printf("%s\n",flag?"YES":"NO");
    }
    return 0;
}

C题是个很好的题:

有无数种方法可以过的:线段树+并查集+STL的set

并查集的搞法:维护每个区间的大小

#include<bits/stdc++.h>
using namespace std;

#define LL __int64
const int maxn=1e5+50;

LL a[maxn],ans[maxn];
int pos[maxn],n;
bool vis[maxn];

int fa[maxn];
int getfa(int x){
    if (fa[x]==-1) return x;
    return fa[x]=getfa(fa[x]);
}

void combine(int x,int y){
    x=getfa(x);
    y=getfa(y);
    if (x!=y){
        fa[x]=y;
        a[y]+=a[x];
    }
}

int main(){
    //freopen("input.txt","r",stdin);
    while(scanf("%d",&n)!=EOF){
        for(int i=1;i<=n;i++) scanf("%I64d",&a[i]);
        for(int i=1;i<=n;i++) scanf("%d",&pos[i]);
        memset(vis,false,sizeof(vis));
        memset(ans,0,sizeof(ans));
        memset(fa,-1,sizeof(fa));
        LL temp=0;
        for(int i=n;i>=1;i--){
            ans[i]=temp;
            int x=pos[i];
            vis[x]=true;
            if (vis[x-1]) combine(x-1,x);
            if (vis[x+1]) combine(x+1,x);
            temp=max(temp,a[getfa(x)]);
        }
        for(int i=1;i<=n;i++)
            cout<<ans[i]<<endl;
    }
    return 0;
}

set的搞法:

单纯的模拟:当前对分隔数组的影响:其实是对前缀和的分隔

我只需要找到离我最近的两次分割,我再来分隔一次就好

第一个set:保存当前已经分了哪些地方

第二个set:当前分隔之后的各个区间段的值是多少

之所以要用multiset:是因为可能会有重复的区间和值

#include<bits/stdc++.h>
using namespace std;

#define LL __int64

const int maxn=1e5+50;
int n,x;
LL a[maxn],L,R;
multiset<LL> s,res;
multiset<LL>::iterator it;

int main(){
	//freopen("input.txt","r",stdin);
	while(scanf("%d",&n)!=EOF){
		s.clear();res.clear();a[0]=0;
		for(int i=1;i<=n;i++){
			scanf("%d",&a[i]);
			a[i]+=a[i-1];
		}
		a[n+1]=a[n];
		res.insert(a[n]);
		s.insert(0);s.insert(n+1);
		for(int i=1;i<=n;i++){
			scanf("%d",&x);
			it=s.lower_bound(x);
			R=*it;
			it--;
			L=*it;
			s.insert(x);
			res.erase(res.find(a[R-1]-a[L]));
			res.insert(a[x-1]-a[L]);
			res.insert(a[R-1]-a[x]);
			cout<<*--res.end()<<endl;
		}
	}
	return 0;
}

再来个线段树题解的链接:

线段树题解


D:一个数学思维的好题

因为集合中元素是固定的,所以一定是一对一的产生的

如果x能够产生y,按照题目中的规则,必有y/2==x成立

那么,我们可以二分答案(最大值的数是多少)

然后集合中的每个值不断除2,既不能为0,也不能重复,如果能够找到的话,就是个可能性的答案

#include<bits/stdc++.h>
using namespace std;

const int maxn=5e4+50;
int a[maxn],tmp[maxn],ans[maxn];
map<int,bool> vis;
int n;

bool check(int x){
    vis.clear();
    for(int i=1;i<=n;i++){
        tmp[i]=a[i];
        while(tmp[i]>x) tmp[i]/=2;
        if (tmp[i]==0) return false;
        while(tmp[i]&&vis[tmp[i]]) tmp[i]/=2;
        if (tmp[i]==0) return false;
        vis[tmp[i]]=true;
    }
    return true;
}

int main(){
    freopen("input.txt","r",stdin);
    while(scanf("%d",&n)!=EOF){
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        int L=1,R=2e9,mid;
        while(L<=R){
            mid=(L+R)>>1;
            if (check(mid)){
                for(int i=1;i<=n;i++) ans[i]=tmp[i];
                R=mid-1;
            }
            else
                L=mid+1;
        }
        for(int i=1;i<=n;i++)
            printf("%d%c",ans[i],i==n?'\n':' ');
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值