您的位置:首页 > 其它

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

2016-10-03 11:01 369 查看
这套题其实应该是个上分场的,然后第一次锁了之后被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
;
res.insert(a
);
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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐