• BZOJ#3267. KC采花



    3267: KC采花

    Time Limit: 30 Sec  Memory Limit: 256 MB
    Submit: 152  Solved: 107

    Description

    KC在公园里种了一排花,一共有n朵,游手好闲的KC常常在公园里采花。他对每朵花都有一个美丽度鉴赏。由于对
    花的喜好不同,有的花分数很高,有的花分数很低,甚至会是负数。KC很忙,每次采花的时候,不可能从第一朵花
    ,走到第n朵。所以他会先选定一个区间[l,r](1<=l<=r<=n),作为当天的采花范围。同时为了方便采花,他总是从
    [l,r]中最多选出k个互不相交的子区间,将这些子区间的花全部采光。当然,他希望美丽度总和最大。KC对花的鉴
    赏随着他对世界观人生观的改变而改变,他会不时地对每朵花的美丽度进行修改,可能改低,也可能提高。KC的行
    为持续m天,每天的行为要么是采花,要么是改变花的美丽度。注:(1)[l,r]的最多k个互不相同子区间可以表示成
    :[x1,y1],[x2,y2],...,[xt,yt],满足l<=x1<=y1<x2<=y2<...<xt<=yt<=r,且0<=t<=k。(2)由于是KC种的花,一
    朵花采掉第二天会立刻生出来。

    Input

    第一行一个正整数n,n<=100000。
    第二行n个整数a1,a2...an,表示n朵花的美丽度。|ai|<=500。
    第三行一个正整数m,m<=100000。
    第四行开始,接下来m行,每行表示该天KC的行为。
    修改美丽度的行为用0 i val描述,表示将ai修改为val,|val|<=500。
    采花行为用1 l r k描述,k<=20意义如题面。
    n,m<=50000,k<=20,ai以及修改的val的绝对值不超过500。

    Output

    对于每个采花行为,每行一个整数表示最大的美丽度总和

    Sample Input

    9
    9 -8 9 -1 -1 -1 9 -8 9
    3
    1 1 9 1
    1 1 9 2
    1 4 6 3

    Sample Output

    17
    25
    0

    HINT


     题目大意:给一段序列,任意选其中k段,求最大值,支持修改操作。


     分析:考虑暴力怎么求,我们可以想出一个最大费用流的简单模型:

    建边:
    s-S 建边容量为K,费用为0;
    S-每条边 建边容量为1,费用为0;
    每个点拆成两个点 建边容量为1,费用为a[i]
    每个点可以到到达下一个点,也可以退出。

    求值:

    进行K次增广,可得最后答案。

    不足:

    但是复杂度很高,直接做肯定是不可以的。

    优化:

    我们可以看到,每次他增广都是有规律的,路线是连续的,实际上它每次走的就是最大子序列,
    所以我们可以模拟这个过程,每次选了一段最大序列,就把这个序列取法(模拟网络流的反边)。
    可以用线段树来操作。
    线段树就需要进行,维护最大序列,最小序列(每次要取反),取反,单点修改。

    挺好的一道题,思路挺新颖。


     代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int N=1e5+12;
    
    int n,m;
    int a[N];
    
    struct data
    {
        int lmax,rmax,maxn,sum,p1,p2,pl,pr;
        void add(int x,int y) {p1=p2=pl=pr=x;lmax=rmax=maxn=sum=y;}
    };
    struct Tree
    {
        int l,r,lazy;
        data imax,mini;//为什么要维护最小值,其实不是最小值,
        //而是相当于我们维护的是取反后的序列,我们一起维护,取反时直接交换就ok了 
        void add(int x) {imax.add(l,x);mini.add(l,-x);}
    }tree[N<<2];
    
    inline data merge(data l,data r)
    {
        data now;
        now.sum=l.sum+r.sum;
        now.lmax=max(l.lmax,l.sum+r.lmax);
        now.pl=l.lmax>l.sum+r.lmax?l.pl:r.pl;
        now.rmax=max(r.rmax,r.sum+l.rmax);
        now.pr=r.rmax>r.sum+l.rmax?r.pr:l.pr;
        now.maxn=l.rmax+r.lmax;now.p1=l.pr;now.p2=r.pl;
        if(now.maxn<l.maxn) now.maxn=l.maxn,now.p1=l.p1,now.p2=l.p2;
        if(now.maxn<r.maxn) now.maxn=r.maxn,now.p1=r.p1,now.p2=r.p2;
        return now;
    }
    void pushup(int now)
    {
        tree[now].imax=merge(tree[now<<1].imax,tree[now<<1|1].imax);
        tree[now].mini=merge(tree[now<<1].mini,tree[now<<1|1].mini);
    }
    void pushdown(int now)
    {
        if(!tree[now].lazy||tree[now].l==tree[now].r) return ;
        swap(tree[now<<1].mini,tree[now<<1].imax);
        swap(tree[now<<1|1].mini,tree[now<<1|1].imax);
        tree[now<<1].lazy^=1;tree[now<<1|1].lazy^=1;
        tree[now].lazy^=1;
    }
    void build(int l,int r,int now)
    {
        tree[now].l=l;tree[now].r=r;tree[now].lazy=0;
        if(l==r) {tree[now].add(a[l]);return ;}
        int mid=(l+r)>>1;
        build(l,mid,now<<1);build(mid+1,r,now<<1|1);
        pushup(now);
    }
    
    void updata(int L,int C,int now)
    {
        if(tree[now].l==tree[now].r) {tree[now].add(C);return ;}
        int mid=(tree[now].l+tree[now].r)>>1;
        pushdown(now);
        if(L<=mid) updata(L,C,now<<1);
        else updata(L,C,now<<1|1);
        pushup(now);
    }
    void Reverse(int L,int R,int now)
    {
        if(L<=tree[now].l&&R>=tree[now].r) {swap(tree[now].imax,tree[now].mini);tree[now].lazy^=1;return ;}
        int mid=(tree[now].l+tree[now].r)>>1;
        pushdown(now);
        if(L<=mid) Reverse(L,R,now<<1);
        if(R>mid) Reverse(L,R,now<<1|1);
        pushup(now);
    }
    inline data query(int L,int R,int now)
    {
        if(L==tree[now].l&&R==tree[now].r) return tree[now].imax;
        int mid=(tree[now].l+tree[now].r)>>1;
        pushdown(now);
        if(R<=mid) return query(L,R,now<<1);
        if(L>mid) return query(L,R,now<<1|1);
        return merge(query(L,mid,now<<1),query(mid+1,R,now<<1|1));
    }
    
    void Updata(int L,int C) {updata(L,C,1);}
    struct Stack{int l,r;}sta[25];
    void Query(int L,int R,int K)
    {
        int ans=0,temp=0;
        while(K--)
        {
            data t=query(L,R,1);
            if(t.maxn>0) ans+=t.maxn;else break;
            Reverse(t.p1,t.p2,1);
            sta[++temp]=(Stack){t.p1,t.p2};
        }
        while(temp) Reverse(sta[temp].l,sta[temp].r,1),temp--;//求完之后要还原回去  
        printf("%d
    ",ans);
    }
    
    int main()
    {
        freopen("a.in","r",stdin);
        scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        build(1,n,1);
        scanf("%d",&m);
        int type,l,r,x;
        for(int i=1;i<=m;i++)
        {
            scanf("%d",&type);
            if(type==0) scanf("%d%d",&l,&x),Updata(l,x);
            else scanf("%d%d%d",&l,&r,&x),Query(l,r,x);
        }
        return 0;
    }
    View Code

  • 相关阅读:
    如何培养编程所需要的逻辑思维?
    CSS教程
    Android中Service(服务)详解
    Tomcat热部署的实现原理
    Java多线程和线程池(转)
    导出Excel表格
    各种时间格式化的转化
    上传多媒体文件到微信公众平台
    发起https请求并获取结果
    Java 将字节转换为十六进制字符串
  • 原文地址:https://www.cnblogs.com/Heey/p/8962398.html
Copyright © 2020-2023  润新知