codeforces - 1041 ABCDE 题解

A - Heist 

签到,输出max(ai) - min (ai) +1-n;

 

B - Buying a TV Set 

签到,x/= gcd(x,y),y/= gcd(x,y), 答案为min(a/x,b/y);

 

C - Coffee Break

 将n个休息时间点分到m天,每天每个时间点之间的间隔大于d,求m最小值

 做法很多,思路类似。自己的乱搞的做法是从小到大排序,枚举ai,用upper_bound找大于   ai+d的第一个数,标记成和ai同一天。问题是满足ai+d 的最小的数可能已经被前面的数标记   了。由于ai + d>= ai-1 + d,可以用变量tmp保存之前标记的最后一个数的下标,如果当前查   找的数的下标小于等于tmp,则令a[tmp+1]被标记即可,并更新tmp。复杂度是O(nlogn)

 或者就是用单调队列处理,过程类似,时间复杂度O(n)

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<vector>
#include<stack>
#include<bitset>
#include<cstdlib>
#include<cmath>
#include<set>
#include<list>
#include<deque>
#include<map>
#include<queue>
using namespace std;
typedef long long ll;
const int mx = 5e5+50;
struct node{
    ll v;
    int pos;
}a[mx];
ll b[mx];

bool cmp(node x,node y){
    return x.v < y.v;
}

int vis[mx],ans[mx];
int main (){
    int n,m; ll d;
    cin >>n>>m>>d;
    for (int i=1;i<=n;i++){
        scanf ("%lld",&a[i].v);
        a[i].pos=i;
    }
    int tmp =0;
    sort (a+1,a+1+n,cmp);
    for (int i=1;i<=n;i++) b[i]=a[i].v;
    int tot=0;
    for (int i=1;i<=n;i++){
        if (!vis[i]) vis[i]=++tot;
        int id=upper_bound(b+1,b+1+n,b[i]+d)-b;
        if (id>n) continue;
        //printf("第%d个数 %d %d\n",i,id,a[id].v);
        if (id>tmp){
            tmp = id;
            vis[tmp]=vis[i];
        }
        else {
            vis[++tmp]=vis[i];
        }
    }
    printf("%d\n",tot);
    for (int i=1;i<=n;i++){
        ans[a[i].pos] = vis[i];
    }
    for (int i=1;i<n;i++) printf("%d ",ans[i]);
    printf("%d\n",ans[n]);
    return 0;
}

 

D - Glider

 比较裸的前缀和加二分

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<vector>
#include<stack>
#include<bitset>
#include<cstdlib>
#include<cmath>
#include<set>
#include<list>
#include<deque>
#include<map>
#include<queue>
using namespace std;
typedef long long ll;
const int mx=4e5+10;
struct node{
    ll l,r;
}s[mx];
bool cmp(node a,node b){
    return a.l <b.l;
}
ll dis[mx];
ll val[mx];
int main (){
    int n; ll h;
    cin >> n>>h;
    for (int i=1;i<=n;i++){
        scanf ("%lld %lld",&s[i].l,&s[i].r);
    }
    sort (s+1,s+1+n,cmp);
    dis[1]=0;
    val[1]=s[1].r-s[1].l;
    for (int i=2;i<=n;i++){
        dis[i]=s[i].l - s[i-1].r;
        dis[i]+=dis[i-1];
        val[i]= s[i].r- s[i].l;
        val[i]+=val[i-1];
        //printf("dis = %lld , val = %lld\n\n",dis[i],val[i]);
    }
    ll ans = 0;
    for (int i=1;i<=n;i++){
        int pos=lower_bound(dis+1,dis+1+n,h+dis[i])-dis;
        //printf("开始于第%d段\n",i);
        //printf("结束于第%d段后",pos);
        ll tmp = val[pos-1]-val[i-1];
        //printf("%lld\n",tmp);
        ans = max (tmp,ans);
    }
    printf("%lld\n",ans+h);
    return 0;
}

 

E - Tree Reconstruction

构造题,给的是删掉每条边形成的两个分开的联通块各自最大的节点的序号,让你重构这颗树。

容易想到的删掉任何一条边留下的一个联通块最大的点是n。删除连接叶节点和树的一条边,那叶节点就是给出的一个最大值。

trick:没构造思路就画画看,会有发现。

如果有一个节点多次在删边后成为一个最大值,删的这几条边会在这个节点到最大点n的一条链上。

于是想到干脆把n当作根节点,去搞这棵树。一个点x成为最大值的次数记作cnt[x]次,在这棵树上把它和根n连cnt[x]条边,然后它到根这条链上的祖先节点u应该是小于x且cnt[u]=0;

发现这些后就知道构造方法了。从遍历点i:n-1——1,如果cnt[i]>0,就遍历点j:i-1——1,找cnt[i]-1个cnt[j] = 0的点j,连一条链到根上。条件满足即可构造出这么一颗根连了很多根链的树,不满足则输出答案“NO”。

 

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<vector>
#include<stack>
#include<bitset>
#include<cstdlib>
#include<cmath>
#include<set>
#include<list>
#include<deque>
#include<map>
#include<queue>
using namespace std;
const int mx =1e3+50;
int cnt[mx];
int a[mx],b[mx];
struct node{
    int u,v;
}edge[mx];
int vis[mx];
int main (){
    int n;
    cin >>n;
    for (int i=1;i<n;i++){
        int x,y;
        scanf ("%d%d",&x,&y);
        cnt[x]++;cnt[y]++;
    }
    int tot = 0;
    for (int i=n-1;i>=1;i--){
        //printf("%d cnt=%d vis=%d\n",i,cnt[i],vis[i]);
        if (!vis[i] && !cnt[i]){
            printf("NO\n"); return 0;
        }
        if (vis[i]) continue;
        int x = i; int num = 0;
        if (cnt[x]==1){
            edge[++tot].u=x; edge[tot].v=n;
            continue;
        }
        for (int j=i-1;j>=1;j--){
            if (!cnt[j]&& !vis[j]){
                edge[++tot].u=x;
                edge[tot].v=j;
                x=j; vis[j]=1;
                num ++;
            }
            if (num==cnt[i]-1){
                edge[++tot].u=x; edge[tot].v=n; num++;
                break;
            }
        }
        if (num<cnt[i]){
            printf("NO\n"); return 0;
        }
    }
    printf("YES\n");
    for (int i=1;i<=tot;i++){
        printf("%d %d\n", edge[i].u,edge[i].v);
    }
    return 0;
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值