把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

qzezoj 1540 糖果峡谷

题面传送门
三十分......
于是我只能重新想思路。然后就只得了\(35\)分。
考试结束后老师叫我们自己去网上找题解,然而我发现,居然没有任何一篇\(luogu\)题解是正解。于是我准备发一篇题解来\(k\)\(luogu\)所有题解。
正解:
我们先把这个峡谷想象成一个时间轴,每个时间我们最多能带c个糖果。
贪心思路:将右端点从小到大排序,验证这个区间在前面的情况下能不能完成,若能完成,将这一区间内所有点减去当前糖果数。这一定是最优答案。若不能,不去管他。
但这减去又十分费时间,所以我们用线段树来维护其最小值,因为能带的糖果数取决与最小值。
但我们发现一个问题:在糖果消失的时候不能减去时间,所以我们将这个区间变成左闭右开的区间。
这不就很完美的结束了吗?
代码实现:

#include<cstdio>
#include<algorithm>
#define min(a,b) ((a)<(b)?(a):(b))
using namespace std;
int n,m,k,ans,tot,f[2000039],x,y,z,fs[2003009],sum[2000039];
struct yyy {
    int x,y,z;
} a[2000039],tmp;
inline void read(int &x) {
    x=0;
    char s=getchar();
    while(s<'0'||s>'9') s=getchar();
    while(s>='0'&&s<='9') x=(x<<3)+(x<<1)+(s^48),s=getchar();
}
inline bool cmp(yyy a,yyy b) {
    return a.y<b.y;
}
inline void jianshu(int l,int r,int now) {
    if(l==r) {
        sum[now]=k;
        return;
    }
    register int m=(l+r)>>1;
    jianshu(l,m,now<<1);
    jianshu(m+1,r,now<<1|1);
    sum[now]=k;
}
inline void push(int l,int r,int now) {
    register int m=(l+r)>>1;
    f[now<<1]+=f[now];
    sum[now<<1]+=f[now];
    f[now<<1|1]+=f[now];
    sum[now<<1|1]+=f[now];
    f[now]=0;
}
inline void get(int l,int r,int now) {
    if(x<=l&&r<=y) {
        f[now]+=z;
        sum[now]+=z;
        return;
    }
    register int m=(l+r)>>1;
    push(l,r,now);
    if(x<=m) get(l,m,now<<1);
    if(y>m) get(m+1,r,now<<1|1);
    sum[now]=min(sum[now<<1],sum[now<<1|1]);
}
inline int find(int l,int r,int now) {
    if(x<=l&&r<=y)return sum[now];
    register int m=(l+r)>>1,fs=2147483647,ans=2147483647;
    push(l,r,now);
    if(x<=m) fs=find(l,m,now<<1);
    if(y>m) ans=find(m+1,r,now<<1|1);
    return min(fs,ans);
}
int main() {
//  freopen("candy.in","r",stdin);
//  freopen("candy.out","w",stdout);
    register int i,j;
    read(n);
    read(m);
    read(k);
    for(i=1; i<=n; i++) read(a[i].x),read(a[i].y),read(a[i].z),a[i].y+=a[i].x-1;
    sort(a+1,a+n+1,cmp);
    jianshu(1,m,1);
    for(i=1; i<=n; i++) {
        tmp=a[i];
        x=tmp.x;
        y=tmp.y-1;
        ans=find(1,m,1);
        tot+=min(ans,tmp.z);
        z=-min(ans,tmp.z);
        get(1,m,1);
        //for(j=1;j<=m;j++) x=y=j,printf("%d ",find(1,m,1));printf("\n");
    }
    printf("%d",tot);
    return 0;
}

但是线段树常数很大,于是要卡常。
我把\(sort\)手写,加寄存器......终于卡过了时限:\(984ms\)!
代码实现:

#include<cstdio>
#pragma GCC optimize(2)
#pragma GCC optimize("inline")
using namespace std;
int n,m,k,ans,tot,f[2000039],x,y,z,fs[2003009],sum[2000039];
struct yyy {
    int x,y,z;
    bool operator<(register const yyy &x) const {
        return y< x.y;
    }
} a[2000039];
inline void read(register int &x) {
    x=0;
    register char s=getchar();
    while(s<'0'||s>'9') s=getchar();
    while(s>='0'&&s<='9') x=(x<<3)+(x<<1)+(s^48),s=getchar();
}
inline void push(register int l,register int r,register int now) {
    register int m=(l+r)>>1;
    f[now<<1]+=f[now];
    sum[now<<1]+=f[now];
    f[now<<1|1]+=f[now];
    sum[now<<1|1]+=f[now];
    f[now]=0;
}
inline void get(register int l,register int r,register int now) {
    if(x<=l&&r<=y) {
        f[now]+=z;
        sum[now]+=z;
        return;
    }
    register int m=(l+r)>>1;
    push(l,r,now);
    if(x<=m) get(l,m,now<<1);
    if(y>m) get(m+1,r,now<<1|1);
    sum[now]=sum[now<<1]<sum[now<<1|1]?sum[now<<1]:sum[now<<1|1];
}
inline int find(register int l,register int r,register int now) {
    if(x<=l&&r<=y)return sum[now];
    register int m=(l+r)>>1,fs=2147483647,ans=2147483647;
    push(l,r,now);
    if(x<=m) fs=find(l,m,now<<1);
    if(y>m) ans=find(m+1,r,now<<1|1);
    return fs<ans?fs:ans;
}
inline void sort(register int l,register int r)
{
    register yyy mid=a[(l+r)>>1],s;
    register int i=l,j=r;
    do{
        while(a[i]<mid) i++;
        while(mid<a[j]) j--;
        if(i<=j){
            s=a[j];a[j]=a[i];a[i]=s;
            i++;j--;
        }
    }while(i<=j);
    if(l<j) sort(l,j);
    if(i<r) sort(i,r);
}
int main() {
//  freopen("candy.in","r",stdin);
//  freopen("candy.out","w",stdout);
    register int i,j;
    register yyy tmp;
    read(n);
    read(m);
    read(k);
    for(i=1; i<=n; i++) read(a[i].x),read(a[i].y),read(a[i].z),a[i].y+=a[i].x-1;
    sort(1,n);
    for(i=1;i<=4*m;i++) sum[i]=k;
    for(i=1; i<=n; i++) {
        x=a[i].x;
        y=a[i].y-1;
        ans=find(1,m,1);
        z=-(ans<a[i].z?ans:a[i].z);
        tot+=-z;
        get(1,m,1);
        //for(j=1;j<=m;j++) x=y=j,printf("%d ",find(1,m,1));printf("\n");
    }
    printf("%d",tot);
    return 0;
}

然而正解是\(zkw\)线段树?
算了,去看\(rsj\)大佬的\(zkw\)线段树吧

posted @ 2020-03-20 18:20  275307894a  阅读(85)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end