codeforces 833E. Caramel Clouds

本文介绍了一种使用线段树解决区间覆盖问题的方法,通过维护单个和交叉云覆盖的区间长度来优化最大连续晴天区间的计算。该算法通过扫描坐标轴上的云朵并维护相关数据结构来实现高效求解。

英文不好..不是很懂题解里提到的那颗treap是干嘛的,还有题解说的维护一棵树,子树里什么single最大的和x相邻什么的没太看懂他在说什么…..但是yy一下然后发现可以用线段树直接弄…

因为最多消除两片云,可以发现当一个区域被两片云以上覆盖后不可能产生贡献,
令single[x]表示只被x覆盖的区域长度,
cross[x][y]代表只被x和y覆盖的区域长度,
opt[x]代表去掉的云一定含x,最多去掉多少阴天(其实根据一些对opt的更新,也可以认为opt[x]去掉的另一个云右端点一定不在x后面)
Free代表没有被云覆盖的长度,
Top代表当前opt的最大值,
将一朵云差分,在坐标轴上扫,维护一个覆盖当前的云的set,加入一个断点时枚举集合中元素个数为0,1,2,>2,的情况讨论维护以上值
个数=0的时候,更新Free即可
元素个数>2的时候不用管
=2的时候,更新一下这两朵云的cross,并尝试更新他们的opt
个数=1的时候,更新single,因为cross在更新的时候一定会更新到他,所以只要考虑和他没有交集的云,先将云按照代价排序后,二分一下他还能和哪些云组合(记得除掉他自己),然后找这些云里面single最大的尝试更新他的opt

上述过程中顺便更新一下Top

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;

inline void read(int &x)
{
    char c; while(!((c=getchar())>='0'&&c<='9'));
    x=c-'0';
    while((c=getchar())>='0'&&c<='9') (x*=10)+=c-'0';
}
inline void up(int &x,const int &y){if(x<y)x=y;}
const int maxn = 310000;
const int maxm = 310000;

int n,m,C;
struct cloud
{
    int l,r,c;
}s[maxn];
inline bool cmpc(const cloud x,const cloud y){return x.c<y.c;}
struct Query
{
    int i,need;
}q[maxn];
inline bool cmpq(const Query x,const Query y){return x.need<y.need;}
struct node
{
    int p,v,i;
    node(){}
    node(const int _p,const int _v,const int _i){p=_p;v=_v;i=_i;}
}a[maxn<<1]; int tot;
inline bool cmpn(const node x,const node y){return x.p<y.p;}
int seg[maxn*40],single[maxn];
int loc;
void upd(const int x,const int l,const int r)
{
    if(l==r) { seg[x]=single[l]; return; }
    int mid=l+r>>1,lc=x<<1,rc=lc|1;
    if(loc<=mid) upd(lc,l,mid);
    else upd(rc,mid+1,r);
    seg[x]=max(seg[lc],seg[rc]);
}
int lx,rx;
int search(const int x,const int l,const int r)
{
    if(l==r) return l;
    int mid=l+r>>1,lc=x<<1,rc=lc|1;
    if(seg[lc]>seg[rc]) return search(lc,l,mid);
    else return search(rc,mid+1,r);
}
int query(const int x,const int l,const int r)
{
    if(rx<l||r<lx) return 0;
    if(lx<=l&&r<=rx) return search(x,l,r);
    int mid=l+r>>1,lc=x<<1,rc=lc|1;
    int t1=query(lc,l,mid),t2=query(rc,mid+1,r);
    return single[t1]>single[t2]?t1:t2;
}
map<int,int>cross[maxn];
set<int>S; int sn;
set<int>::iterator it;

int opt[maxn],Free,Top;

int sum(int x,int y)
{
    if(x>y) swap(x,y);
    return single[x]+single[y]+cross[x][y];
}
int ans[maxm];
void solve()
{
    S.insert(0);
    Free=Top=0;
    int now=0,pos=1;
    for(int i=1;i<=tot;i++)
    {
        int delta=a[i].p-now; now=a[i].p;
        if(!sn) Free+=delta;
        else if(sn==1)
        {
            it=S.upper_bound(0); int x=(*it);
            single[x]+=delta; loc=x; upd(1,1,n);
            opt[x]+=delta;

            int oth=C-s[x].c;
            if(oth>=0)
            {
                int u=single[x];
                if(oth>=s[1].c)
                {
                    int l=1,r=n;
                    while(l<=r)
                    {
                        int mid=l+r>>1;
                        if(s[mid].c<=oth) l=mid+1;
                        else r=mid-1;
                    }lx=1,rx=l-1;
                    if(x==rx) rx--;
                    if(x<rx) { lx=x+1; if(lx<=rx) up(u,sum(x,query(1,1,n))); lx=1,rx=x-1; }
                    if(lx<=rx) up(u,sum(x,query(1,1,n))); 
                }
                up(opt[x],u);
                up(Top,opt[x]);
            }
        }
        else if(sn==2)
        {
            it=S.upper_bound(0); int x=(*it); 
            it++; int y=(*it);
            if(cross[x].count(y)>0) cross[x][y]+=delta;
            else cross[x][y]=delta;
            if(s[x].c+s[y].c<=C)
            {
                up(opt[x],sum(x,y)); up(opt[y],sum(x,y));
                up(Top,opt[x]);
            }
        }
        while(pos<=m&&Top+Free>=q[pos].need) ans[q[pos].i]=now-(Top+Free-q[pos].need),pos++;
        if(pos>m) break;

        if(a[i].v==1) S.insert(a[i].i),sn++;
        else S.erase(a[i].i),sn--;
    }
}

int main()
{
    read(n); read(C);
    for(int i=1;i<=n;i++)
    {
        read(s[i].l); read(s[i].r); read(s[i].c);
    }sort(s+1,s+n+1,cmpc);
    for(int i=1;i<=n;i++)
    {
        a[++tot]=node(s[i].l,1,i);
        a[++tot]=node(s[i].r,-1,i);
    }sort(a+1,a+tot+1,cmpn);
    a[++tot]=node(2e9+10,1,n+1);

    read(m);
    for(int i=1;i<=m;i++)
    {
        q[i].i=i; read(q[i].need);
    }sort(q+1,q+m+1,cmpq);

    solve();
    for(int i=1;i<=m;i++) printf("%d\n",ans[i]);

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值