Codeforces Round #398 (Div. 2) 题解

题目链接:http://codeforces.com/contest/767

A. Snacktower

题意:给你n个数,从1到n乱序,让你从大到小输出,而且要根据先入后出的顺序,输出n行,如果当前行无法输出就直接换行再满足条件行一起输出。

解法:直接开两个数组,一个数组存储n个数,;另外一个数组判断这个数是否已经存入来输出。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 100005;
int n, a[maxn], b[maxn];
int main(){
    scanf("%d", &n);
    int t = n;
    for(int i=1; i<=n; i++){
        scanf("%d", &a[i]);
        b[a[i]] = 1;
        for(;b[t]==1;t--) printf("%d ",t);
        printf("\n");
    }
}

B. The Queue

题意:有个人需要办理一个证件,证件办理处开门的时间是ta到tb,办理一个证件需要t的时间,现在有n个人,知道n个人来的时间,求这个办理证件的人在什么时间来等待的时间最少

解法:肯定要在某个人前一秒到达,到的更早没有意义,到的更晚就被他抢先了。 这样可以 O(n) 枚举一遍解决。 但是要考虑各种情况,比如在所有人之后到、别人到的时间都不在规定时间内。

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

int main(){
    LL a, b, t;
    scanf("%lld %lld %lld", &a,&b,&t);
    LL maxx = 1e12;
    LL ans = 0;
    int n;
    scanf("%d", &n);
    while(n--){
        LL k;
        scanf("%lld", &k);
        if(k<=b-t){
            if(k&&max(a,k-1)<=b-t&&(a-k+1)<maxx){
                maxx = a-k+1;
                ans = min(a,k-1);
            }
            a=max(a,k)+t;
        }
    }
    if(a<=b-t) ans=a;
    return 0*printf("%lld\n", ans);
}

C. Garland

题意:给你一颗有n个节点的树,每个节点都有一个值,问你是否能删去两条边使删后的三块值的和都相等

解法:首先判断所有数的和是不是能整除3,能的话直接DFS子树的和能得到sum/3的点,得到一个之后标记这个子树不可用,再DFS一遍即可。

#include <bits/stdc++.h>
using namespace std;
const int inf = 0x7fffffff;
const int maxn = 1000010;
const double eps = 1e-9;
typedef long long LL;
inline int read()
{
    char ch=getchar();
	int f=1,x=0;
    while(!(ch>='0'&&ch<='9')){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+(ch-'0');ch=getchar();}
    return x*f;
}
inline LL llread(){
    char ch=getchar();
    LL f=1,x=0;
    while(!(ch>='0'&&ch<='9')){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+(ch-'0');ch=getchar();}
    return x*f;
}
int n, a[maxn], sz[maxn] , head[maxn], edgecnt, sum, ans, vis[maxn], id[maxn];
int chooseedge;
struct edge{
    int v, nxt;
    edge(){}
    edge(int v, int nxt) : v(v), nxt(nxt) {}
}E[maxn*4];
void init(){
    memset(head, -1, sizeof(head)); edgecnt = 0;
}
void addedge(int u, int v){
    E[edgecnt].v = v, E[edgecnt].nxt = head[u], head[u] = edgecnt++;
}
void dfs(int u, int fa){
    sz[u] = a[u];
    for(int i = head[u]; ~i; i = E[i].nxt){
        int v = E[i].v;
        if(v == fa) continue;
        if(v == chooseedge || v == 0) continue;
        dfs(v, u);
        sz[u] += sz[v];
        if(sz[v] == sum){
            printf("%d %d\n", ans + 1, i / 2 + 1);
            exit(0);
        }
    }
}
void dfs1(int u, int fa){
    sz[u] = a[u];
    for(int i = head[u]; ~i; i = E[i].nxt){
        int v = E[i].v;
        if(v == fa) continue;
        if(v == 0) continue;
        dfs1(v, u);
        sz[u] += sz[v];
        if(sz[v] == sum){
            vis[v] = 1;
            id[v] = i / 2;
            ans = id[v];
            chooseedge = v;
            dfs(1, 0);
            puts("-1");
            exit(0);
        }
    }

}
int main(){
    init();
    n = read();
    for(int i = 1; i <= n; i++){
        int u = read(), v = read();
        addedge(u, i);
        addedge(i, u);
        sum += v;
        a[i] = v;
    }
    if(sum % 3 || sum == 0){puts("-1"); return 0;}
    sum /= 3;
    dfs1(1, 0);
}

D. Cartons of milk

题意:主人公很喜欢喝牛奶,每天会喝K盒,如果不足K盒,就会将所有拥有的都喝了。

不傻的人都知道,牛奶过期了之后肯定就不能喝了,那就只能扔掉。

现在冰箱中有N盒牛奶,超市中有M盒牛奶,如果冰箱中的牛奶喝不完就有浪费的情况出现,输出-1,否则输出能够在超时中购买的牛奶数量的最大值。

并且输出可以购买的牛奶的编号。

解法:首先将冰箱的牛奶排升序, 商店的牛奶排降序(商店的要用结构存编号,因为答案要输出)。然后再二分购买牛奶的瓶数。测试牛奶是否喝的完的时候,一瓶一瓶地模拟就是了,喝完了k瓶,然后又到了新的一天。复杂度O(mlogm)

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6+10;
struct node{
    int x, id;
    bool operator<(const node &rhs){
        return x<rhs.x;
    }
}b[maxn];
int n, m, k, a[maxn];
bool check(int p2){
    int p1=1, now=0;
    while(p1<=n||p2<=m){
        if((p1<=n&&a[p1]<now)||(p2<=m&&b[p2].x<now)) return 0;//过期
        for(int i=1; i<=k; i++)
            if(p1<=n&&(p2>m||a[p1]<b[p2].x)) p1++;
            else p2++;
        now++;
    }
    return 1;
}
int main(){
    scanf("%d %d %d", &n,&m,&k);
    for(int i=1; i<=n; i++) scanf("%d",&a[i]);
    for(int i=1; i<=m; i++) scanf("%d",&b[i].x), b[i].id=i;
    sort(a+1,a+n+1);
    sort(b+1,b+m+1);
    if(!check(m+1)) return 0*puts("-1");
    //puts("233");
    int l=1,r=m+1,ans;
    while(l<=r){
        int mid=(l+r)/2;
        if(check(mid)) ans=mid,r=mid-1;
        else l=mid+1;
    }
    printf("%d\n", m-ans+1);
    for(int i=ans; i<=m; i++) printf("%d ",b[i].id);
    printf("\n");
}

E. Change-free

题意:

一共有两种货币:100块的和1块的.一开始有m个1块的,和无限个100块的.

一共有N天,其中每天的饭钱是ci.每天收银员的怒气值是wi.

对应一天收银员不高兴的值会增长:找零钱的数量(100块的和1块的找零总数)*wi.

收银员很聪明,他肯定给你找零是最小的数量。

就是说假如今天饭钱是165.假如你给他300,他一定找你一张100的和35个一块的。

这N天是按照时间顺序给出的,不能打乱,问最终收银员这N天积累的不高兴值最高是多少。

对应每天给收银员的货币情况是什么。

解法:

http://blog.csdn.net/mengxiang000000/article/details/66975619

一个非常神奇的贪心。。。

1、

①首先,如果m>=a[i].ci%100.那么对应这天直接用1块a[i].ci%100个.以及100块a[i].ci/100个。

②否则,如果遇到了m<a[i].ci%100的情况,那么只有两种弥补方式,要么这一天给a[i].ci/100+1个100块的货币,要么在之前某些天来凑够剩余的硬币数量tmp(就是说在之前某些天用整数钱换一堆零钱到这一天用).使得tmp+m>=a[i].ci%100.


2、那么这个“有些天”如何控制呢?想了很久,后来才发现一个关键点:对于之前的某天,如果他选择的方式①支付,那么他那天就支付了a[那天].ci%100个1块的硬币,那么如果我们那天改变成②方式支付,那么相当于到这一天,多了100硬币出来,我们肯定一点,100+m是一定>=a[i].ci%100的,所以只要找到之前某一天,选择的方式①支付,我们改成方式②去支付,就能够使得今天的1块钱够用。

对于出现了m<a[i].ci%100的情况,其一定选择【0~i】中的某天,从方式①支付变成了方式②支付.使得1块钱的硬币足够用。

这里贪心的去想,肯定想要选择的那天收银员增长的不高兴值尽可能的小,那么这里我们维护一个优先队列即可。


关键点在于想到如何替换,替换在贪心里面真的是很实用的技巧。。贪心要多往这方面想。


#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 1e5+10;
struct node{
    LL ci,wi,cmp,id;
    bool operator<(const node &rhs) const{
        return cmp>rhs.cmp;
    }
}a[maxn];
LL n, m, sum, use[maxn], ans[maxn][2];

int main(){
    scanf("%lld %lld", &n,&m);
    memset(use, false, sizeof(use));
    priority_queue <node> q;
    for(int i=1; i<=n; i++) scanf("%lld", &a[i].ci);
    for(int i=1; i<=n; i++) scanf("%lld", &a[i].wi);
    for(int i=1; i<=n; i++){
        a[i].cmp = (100-(a[i].ci%100))*a[i].wi;
        a[i].id = i;
        ans[i][0] = a[i].ci/100;
        ans[i][1] = a[i].ci%100;
        if(ans[i][1] != 0) q.push(a[i]);
        else use[i] = 1;
        if(m>=a[i].ci%100){
            m-=a[i].ci%100;
        }else{
            node now=q.top();
            sum += now.cmp;
            m -= ans[i][1];
            m += 100;
            q.pop();
        }
    }
    while(!q.empty()){
        use[q.top().id] = 1;
        q.pop();
    }
    printf("%lld\n", sum);
    for(int i=1; i<=n; i++){
        if(use[i] == 0) ans[i][0]++,ans[i][1]=0;
        printf("%lld %lld\n", ans[i][0],ans[i][1]);
    }
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值