zoukankan      html  css  js  c++  java
  • 牛客模拟赛 下雨天 题解

    算是我见过肥肠优美的一道题了呢qvq

    链接:https://ac.nowcoder.com/acm/contest/1105/B
    来源:牛客网

    题目描述

    小多有n个池塘,第i个池塘容量为i,一开始都没有水。

    随后的很多天,第i天的天气状况决定了所有水池同时的水量变化bib_ibibi>0b_i > 0bi>0表示在下雨,反之则表示在干旱。

    如果某个池塘满了,天还在下雨,那么多余的水就会白白流失掉。同样的,如果池塘干了还在干旱,池塘也不会出现负水量。更正式地说,设第k个水池当前水量为u,经历了某天,水量如果增加v,那么该水池在这天过后的水量为min⁡(u+v,k)min( u+v, k )min(u+v,k);若v是个负数(水量减少),那么水量为max⁡(0,u+v)max( 0, u+v )max(0,u+v)。

    对于随后的q天,小多希望知道每一天结束时,所有池塘的总水量。
     
    暴力不解释
    考虑基于n的算法,我们不难想到线段树之类的数据结构。发现每次修改都相当于有一个断点,两边一遍是区间填满/清空,另一边是区间改值,维护三个lazytag即可。中间的断点二分找到,如果利用类似线段树K大的方法可以做到qlogn,大概能跑个7/80 (但是我不会写于是写了qlog^2n的二分
    代码很长 但是没啥细节
     
    #include <cstdio>
    #include <iostream>
      
    #define int long long int
      
    #define rit register int
      
    using namespace std;
      
    inline int read() {
        int x=0,f=1;
        char cr=getchar();
        while (cr>'9' || cr<'0') {
            if (cr=='-') f=-1;
            cr=getchar();
        }
        while (cr>='0' && cr<='9') {
            x=(x<<3)+(x<<1)+cr-'0';
            cr=getchar();
        }
        return x*f;
    }
      
    const int maxn=1000050;
      
    bool Full[maxn<<2],Empty[maxn<<2];
    int tr[maxn<<2],tag[maxn<<2];
      
    inline int full_sum(int l,int r) {
        int x=l+r,y=r-l+1;
        if (x&1) return y/2*x;
        return x/2*y;
    }
      
    inline void add(int now,int l,int r,int w) {  
        tr[now]+=(r-l+1)*w;
        tag[now]+=w;
    }
      
    inline void ful(int now,int l,int r) {
        tr[now]=full_sum(l,r),Empty[now]=0,tag[now]=0;
        Full[now]=1;
    }
      
    inline void emp(int now,int l,int r) {
        tr[now]=0,Full[now]=0,tag[now]=0;
        Empty[now]=1;
    }
      
    inline void pushdown(int now,int l,int r) {
        int mid=l+r>>1;
        if (Empty[now]) {
            emp(now<<1,l,mid);
            emp(now<<1|1,mid+1,r);
            Empty[now]=0;
        }
        if (Full[now]) {
            ful(now<<1,l,mid);
            ful(now<<1|1,mid+1,r);
            Full[now]=0;
        }
        if (tag[now]) {
            add(now<<1,l,mid,tag[now]);
            add(now<<1|1,mid+1,r,tag[now]);
            tag[now]=0;
        }
    }
      
    inline void paint(bool flag,int now,int l,int r,int x,int y) {
        if (x>y) return;
        if (x<=l && r<=y) {
            if (flag==0) return ful(now,l,r);
            if (flag==1) return emp(now,l,r);
        }
        int mid=l+r>>1;
        pushdown(now,l,r);
        if (x<=mid) paint(flag,now<<1,l,mid,x,y);
        if (mid+1<=y) paint(flag,now<<1|1,mid+1,r,x,y);
        tr[now]=tr[now<<1]+tr[now<<1|1];
    }
      
      
    inline void modify(int now,int l,int r,int x,int y,int w) {
        if (x>y) return;
        if (x<=l && r<=y) return add(now,l,r,w);
        int mid=l+r>>1;
        pushdown(now,l,r);
        if (x<=mid) modify(now<<1,l,mid,x,y,w);
        if (mid+1<=y) modify(now<<1|1,mid+1,r,x,y,w);
        tr[now]=tr[now<<1]+tr[now<<1|1];
    }
      
    inline int query(int now,int l,int r,int loc) {
        if (l==r && l==loc) return tr[now];
        int mid=l+r>>1;
        pushdown(now,l,r);
        if (loc<=mid) return query(now<<1,l,mid,loc);
        if (mid+1<=loc) return query(now<<1|1,mid+1,r,loc);
    }
      
    int w[maxn];
      
    inline void pianfen(int n,int q) {
        bool Full1=0,Empty1=0;
        for (int i=1;i<=q;i++) {
            int temp=read();
            if (temp>=n) {
                printf("%lld
    ",n*(n+1)/2);
                Full1=1;
            }
            if (temp<=-n) {
                printf("0
    ");
                Empty1=1;
            }
            else {
                if (Full1 && temp>=0) {
                    printf("%lld
    ",n*(n+1)/2);
                    continue;
                }
                if (Empty1 && temp<=0) {
                    printf("0
    ");
                    continue;
                }
                else {
                    Full1=Empty1=0;
                    int ans=0;
                    for (int j=1;j<=n;j++) {
                        w[j]+=temp;
                        if (w[j]>j) w[j]=j;
                        if (w[j]<0) w[j]=0;
                        ans+=w[j];
                    }
                    printf("%lld
    ",ans);
                }
            }
        }
    }
      
    signed main() {
        int n=read(),q=read();
        //if (n>1000000) {
        //    pianfen(n,q);
        //    return 0;
        //}
        int lgn=0,tem=n;
        while (tem) tem>>=1,lgn++;
        //if (q * lgn * lgn>=1e8) {
        //    pianfen(n,q);
        //    return 0;
        //}
        int last=0;
        for (rit i=1;i<=q;i++) {
            int temp=read();
            if (temp==0) {
                printf("%lld
    ",last);
                continue;
            }
            if (temp>0) {
                int l=1,r=n;
                int loc=0;
                while (l<=r) {
                    int mid=l+r>>1;
                    if (mid-query(1,1,n,mid)<=temp) l=mid+1,loc=mid;
                    else r=mid-1;
                }
                paint(0,1,1,n,1,loc);
                modify(1,1,n,loc+1,n,temp);
            }
            if (temp<0) {
                int l=1,r=n;
                int loc=n;
                while (l<=r) {
                    int mid=l+r>>1;
                    if (query(1,1,n,mid)>=-temp) r=mid-1,loc=mid;
                    else l=mid+1;
                }
                paint(1,1,1,n,1,loc);
                modify(1,1,n,loc+1,n,temp);
            }
            last=tr[1];
            printf("%lld
    ",tr[1]);
        }
        return 0;
    }
    

    下面我们考虑正解,由于每次都是一个断点,我们可以发现每次都会至多产生一个新的轮廓线段。

    观察到q=1e6 我们有没有办法直接维护这些轮廓呢?

    又观察到无论是v>0还是<0,首先受到影响的一定是左下方的轮廓 也就是最下面的一层梯形。而新增轮廓也是在最下面。我们想到了什么?

    没错 用个栈膜你一下就行了 核心代码10行Orz

    #include <cstdio>
    #include <algorithm>
    
    using namespace std;
    
    inline int read() {
    	int x=0,f=1;
    	char cr=getchar();
    	while (cr>'9' || cr<'0') {
    		if (cr=='-') f=-1;
    		cr=getchar();
    	}
    	while (cr>='0' && cr<='9') {
    		x=(x<<3)+(x<<1)+cr-'0';
    		cr=getchar();
    	}
    	return x*f;
    } 
    
    const int maxn=1000050;
    
    int x[maxn],h[maxn],top;
    int n,q,ans;
    inline int S(int x,int h) {
    	return (n-x)*h + h*(h+1)/2;
    }
    
    int main() {
    	n=read(),q=read();
    	for (int i=1;i<=q;i++) {
    		int v=read();
    		if (v>0) {
    			while (top && h[top]+v>=x[top]) ans-=S(x[top],h[top]),v+=h[top--];
    			h[++top]=min(n,v),x[top]=h[top],ans+=S(x[top],h[top]);
    		}
    		else {
    			while (top && h[top]+v<=0) ans-=S(x[top],h[top]),v+=h[top--];
    			if (top) ans-=S(x[top],h[top]),h[top]+=v,ans+=S(x[top],h[top]);
    		}
    		printf("%lld
    ",ans);
    	}
    	return 0;
    }
    
  • 相关阅读:
    课后作业 day19
    day 18
    博客整理day17 time/datetime/re/random/hashlib/hmac/requests
    博客整理day16 os/sys/json/pickle/logging模块
    博客整理day15 模块
    课后作业 day14
    博客整理day14 匿名函数/内置函数/异常处理/面对象
    博客整理day13
    [Day20]Map接口、可变参数、Collections
    [Day19]Collection接口中的子类(List集合、Set集合)
  • 原文地址:https://www.cnblogs.com/YoOXiii/p/12174081.html
Copyright © 2011-2022 走看看