XOJ2011 fly飞机 题解

XOJ 2011 fly飞机 题解

首先根据题意,我们可将每组人化成一段线段在1~n 的数轴上。
从1~n,从当我们选择右端点靠左的区间的时候方案更优,可以通过画线段图来直观感觉,对于三个相邻线段,右端点靠左的线段设它为第 i 段,后面两个分别是 i+1, i+2,当我们选择 i 的时候,它与 i+2 相交的可能性小于 i+1 ,所以我们不需要牺牲 i 的人数来选择 i+1 的人。所以我们只需要根据各个线段右端点的位置从小到大排序,每次取靠左的。通过线段树维护。
从n~1的反之,根据左端点排序,操作时可将它反过来类似右端点操作。

#include <iostream>
#include <cstdio>
#include <algorithm>
#define _(d) while(d (((ch=getchar())>47)&&ch<58))
#define L(x) (x<<1)
#define R(x) ((x<<1)+1)
using namespace std;
const int Maxk=50005;
const int Maxn=10005;
int k,n,c,tt1,tt2,ans;
inline void Get(int &x){char ch;_(!);x=ch-48;_()x=(x<<3)+(x<<1)+ch-48;}
struct Bnode
{
    int a,b,c;  
}s1[Maxk],s2[Maxk];
struct Btree
{
    int mx,lf,rt;
}t[Maxn<<2];
inline void build(int num,int l,int r)
{
    t[num].lf=l;t[num].rt=r;
    t[num].mx=0;
    if(l+1==r) return;
    int mid=(l+r)>>1;
    build(L(num),l,mid);
    build(R(num),mid,r);
}
inline void update(int num)
{
    t[num].mx=max(t[L(num)].mx,t[R(num)].mx);   
}

inline void insert(int l,int r,int num,int v)
{
    if(t[num].lf+1==t[num].rt) {t[num].mx+=v;return ;}
    int mid=(t[num].lf+t[num].rt)>>1;
    if(r<=mid) insert(l,r,L(num),v);
    else if(l>=mid) insert(l,r,R(num),v);
    else
    {
        insert(l,mid,L(num),v);
        insert(mid,r,R(num),v); 
    }
    update(num);
}
inline int query(int l,int r,int num)
{
    if(l==t[num].lf&&r==t[num].rt) return t[num].mx;
    int mid=(t[num].lf+t[num].rt)>>1;
    if(r<=mid) return query(l,r,L(num));
    if(l>=mid) return query(l,r,R(num));
    else
    {
        return max(query(l,mid,L(num)),query(mid,r,R(num)));    
    }

}
inline bool Bcmp1(const Bnode x,const Bnode y){return x.b<y.b;}
inline bool Bcmp2(const Bnode x,const Bnode y){return x.a>y.a;}
inline void getans(int f)
{
    build (1,1,n+1);
    int tt=f?tt1:tt2;
    if(f)
        for(int i=1;i<=tt;i++)
        {
            int tmp=0;
            tmp=query(s1[i].a,s1[i].b,1);
            tmp=max(0,c-tmp);
            tmp=min(s1[i].c,tmp);
            if(tmp>0)
            {
                ans+=tmp;
                insert(s1[i].a,s1[i].b,1,tmp);
            }
        }
    else
        for(int i=1;i<=tt;i++)
        {
            int tmp=0;
            tmp=query(s2[i].a,s2[i].b,1);
            tmp=max(0,c-tmp);
            tmp=min(s2[i].c,tmp);
            if(tmp>0)
            {
                ans+=tmp;
                insert(s2[i].a,s2[i].b,1,tmp);
            }
        }
}
int main()
{
    //freopen("2011.in","r",stdin);
    int a,b,vv;
    Get(k);Get(n);Get(c);
    for(int i=1;i<=k;i++)
    {
        Get(a);Get(b);Get(vv);
        if(a<b)
        {
            s1[++tt1].a=a;
            s1[tt1].b=b;
            s1[tt1].c=vv;
        }
        else
        {
            s2[++tt2].a=b;
            s2[tt2].b=a;
            s2[tt2].c=vv;
        }
    }
    sort(s1+1,s1+tt1+1,Bcmp1);
    sort(s2+1,s2+tt2+1,Bcmp2);
    getans(1);
    getans(0);printf("%d\n",ans);
    return 0;   
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值