【codeforces #310】div1 A -- C

A题:

题意:给你n个数以k组的形式排列,每组数内保证递增。每次操作可以取出一个数或者把一个数放在一列的最后,问最少经过多少次操作可以使得1--n排列在一列。

分析:首先可以假设若所有数都要先取下,再放到某一列,则要进行2 * n - k - 1次操作。

然后因为若1--i直接在一起,那么可以减少(i - 1)*2次操作,故可得代码。

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int n,k,num,t;
    scanf("%d%d",&n,&k);
    int res = 2 * n - k - 1;
    for(int i = 0 ; i < k ; i ++){
        scanf("%d",&num);
        scanf("%d",&t);
        if(t != 1) for(int j = 1 ; j < num ; j ++) scanf("%d",&t);
        else{
            int flag = 1;
            for(int j = 1 ; j < num ; j ++){
                scanf("%d",&t);
                if(flag && t == j + 1) res -= 2;
                else flag = 0;
            }
        }
    }
    printf("%d\n",res);
    return 0;
}

B题:

题意:给你n个排在一列的岛屿,再给你m个桥,对于任意的i < n,可以在i到i+1之间放置一个大于等于岛中间距离小于等于岛两端距离的桥。问可否有一种方案,使所有相邻岛中间都有一座桥。

分析:对于相邻两岛之间,我们可以得到一个Max[i]和Min[i]分别代表i,i+1两岛之间最大和最小桥的长度。可以先将需求与桥以最小长度排序,对于每个桥,我们可以先将最小值小于等于桥的需求放到一个set里面,然后再set里面取一个最大值最小的,将此桥给它。这必定是最优方案。代码如下:

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 200005;
LL a[N],l[N],r[N];
struct elem{
    LL Max,Min;
    int id;
    void add(LL l1,LL r1,LL l2,LL r2,int _id){
        Max = r2 - l1;
        Min = l2 - r1;
        id = _id;
    }
    bool operator < (const elem &a)const{
        if(Min != a.Min) return Min < a.Min;
        return Max < a.Max;
    }
}ss[N];
struct brige{
    LL val;
    int id;
    bool operator < (const brige &a)const{
        return val < a.val;
    }
    void add(LL _val,int _id){
        val = _val;
        id = _id;
    }
}bb[N];
struct Node{
    LL val;
    int id;
    Node(LL _val = 0,int _id = 0):val(_val),id(_id){}
    bool operator < (const Node &a)const{
        return val > a.val;
    }
};
priority_queue<Node> Q;
int res[N];
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i = 0 ; i < n ; i ++) scanf("%I64d%I64d",&l[i],&r[i]);
    for(int i = 0 ; i < m ; i ++) scanf("%I64d",&a[i]);
    if(m < n - 1){
        printf("No\n");
        return 0;
    }
    for(int i = 0 ; i < n - 1 ; i ++)
        ss[i].add(l[i],r[i],l[i + 1],r[i + 1],i);
    for(int i = 0 ; i < m ; i ++)
        bb[i].add(a[i],i + 1);
    while(!Q.empty()) Q.pop();
    sort(ss,ss + n - 1);
    sort(bb,bb + m);
    int j = 0;
    for(int i = 0 ; i < m ; i ++){
        while(ss[j].Min <= bb[i].val && j < n - 1){
            Q.push(Node(ss[j].Max,ss[j].id));
            j ++;
        }
        while(!Q.empty() && Q.top().val < bb[i].val) Q.pop();
        if(Q.empty()) continue;
        Node u = Q.top();Q.pop();
        res[ u.id ] = bb[i].id;
    }
    int flag = 1;
    for(int i = 0 ; i < n - 1 ; i ++)
        if(res[i] == 0) flag = 0;
    if(flag == 0) printf("No\n");
    else{
        printf("Yes\n");
        for(int i = 0 ; i < n - 1 ; i ++) printf("%d ",res[i]);
        printf("\n");
    }
    return 0;
}

C题:

题意:对于块只有上三角的巧克力,每次从反对角线上开始吃,可以选择向上吃或者向左吃直到遇到空隙。给你一系列操作,问最多可以吃到多少格巧克力。

分析:首先可以考虑到使用线段树维护每一列最多能到那一行和维护每一行最多能到那一列。对于每个询问向上吃的操作,我们可以利用线段树的单点询问,找到能吃到那一行,然后对于吃到的这些块,再对向左吃的那一颗线段树进行update。对于向左吃的操作也可以这样。反复如此统计结果就好了。

还有一种更加巧妙的方法,对于向上吃的方法,可以找到离它最近的一个之前出现过的向上吃的位置记为l1,在找到一个距离它最近的标号比它大的向左吃的操作r1。若r1操作在l1的下方,那么l1不会影响到r1,则吃到r1为止。若r1,在l1的上方,则l1会拦住它或者在拦住l1的操作的上方,故为拦住l1的操作也是拦住此操作的操作。这样就可以知道此次操作吃到了多少块。对于向左吃的方法也是如此。

具体看代码:

选段树版本:

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 200005;
LL a[N],l[N],r[N];
struct elem{
    LL Max,Min;
    int id;
    void add(LL l1,LL r1,LL l2,LL r2,int _id){
        Max = r2 - l1;
        Min = l2 - r1;
        id = _id;
    }
    bool operator < (const elem &a)const{
        if(Min != a.Min) return Min < a.Min;
        return Max < a.Max;
    }
}ss[N];
struct brige{
    LL val;
    int id;
    bool operator < (const brige &a)const{
        return val < a.val;
    }
    void add(LL _val,int _id){
        val = _val;
        id = _id;
    }
}bb[N];
struct Node{
    LL val;
    int id;
    Node(LL _val = 0,int _id = 0):val(_val),id(_id){}
    bool operator < (const Node &a)const{
        return val > a.val;
    }
};
priority_queue<Node> Q;
int res[N];
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i = 0 ; i < n ; i ++) scanf("%I64d%I64d",&l[i],&r[i]);
    for(int i = 0 ; i < m ; i ++) scanf("%I64d",&a[i]);
    if(m < n - 1){
        printf("No\n");
        return 0;
    }
    for(int i = 0 ; i < n - 1 ; i ++)
        ss[i].add(l[i],r[i],l[i + 1],r[i + 1],i);
    for(int i = 0 ; i < m ; i ++)
        bb[i].add(a[i],i + 1);
    while(!Q.empty()) Q.pop();
    sort(ss,ss + n - 1);
    sort(bb,bb + m);
    int j = 0;
    for(int i = 0 ; i < m ; i ++){
        while(ss[j].Min <= bb[i].val && j < n - 1){
            Q.push(Node(ss[j].Max,ss[j].id));
            j ++;
        }
        while(!Q.empty() && Q.top().val < bb[i].val) Q.pop();
        if(Q.empty()) continue;
        Node u = Q.top();Q.pop();
        res[ u.id ] = bb[i].id;
    }
    int flag = 1;
    for(int i = 0 ; i < n - 1 ; i ++)
        if(res[i] == 0) flag = 0;
    if(flag == 0) printf("No\n");
    else{
        printf("Yes\n");
        for(int i = 0 ; i < n - 1 ; i ++) printf("%d ",res[i]);
        printf("\n");
    }
    return 0;
}

set版本:

#include<bits/stdc++.h>
using namespace std;
struct Node{
    int l,lim;
    Node(int l,int lim):l(l),lim(lim){}
    bool operator < (const Node &a)const{
        return l < a.l;
    }
};
set<Node> us,ls;
int main()
{
    char s[10];
    int n,q;
    int x,y;
    us.clear();ls.clear();
    scanf("%d%d",&n,&q);
    us.insert(Node(n + 1,0));
    us.insert(Node(0,0));
    ls.insert(Node(n + 1,0));
    ls.insert(Node(0,0));
    for(int i = 0 ; i < q ; i ++){
        scanf("%d%d%s",&x,&y,s);
        int flag = 0;
        auto It = us.lower_bound(Node(x,0));
        if(It->l == x) flag = 1;
        It = ls.lower_bound(Node(y,0));
        if(It->l == y) flag = 1;
        if(flag){
            printf("0\n");
            continue;
        }
        if(s[0] == 'U'){
            auto It1 = us.lower_bound(Node(x,0));
            auto It2 = ls.lower_bound(Node(y,0));
            It2--;
            if(It2->l > n + 1 - It1->l){
                printf("%d\n",y - It2->l);
                us.insert(Node(x,It2->l));
            }
            else{
                printf("%d\n",y - It1->lim);
                us.insert(Node(x,It1->lim));
            }
        }
        else{
            auto It1 = ls.lower_bound(Node(y,0));
            auto It2 = us.lower_bound(Node(x,0));
            It2 --;
            if(It2->l > n + 1 - It1->l){
                printf("%d\n",x - It2->l);
                ls.insert(Node(y,It2->l));
            }
            else{
                printf("%d\n",x - It1->lim);
                ls.insert(Node(y,It1->lim));
            }
        }
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值