ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

牛客模拟赛 下雨天 题解

2020-01-09 23:55:08  阅读:435  来源: 互联网

标签:牛客 int 题解 top mid 下雨天 while 水量 cr


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

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

题目描述

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

随后的很多天,第i天的天气状况决定了所有水池同时的水量变化bib_ibi​,bi>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*(n+1)/2);
            Full1=1;
        }
        if (temp<=-n) {
            printf("0\n");
            Empty1=1;
        }
        else {
            if (Full1 && temp>=0) {
                printf("%lld\n",n*(n+1)/2);
                continue;
            }
            if (Empty1 && temp<=0) {
                printf("0\n");
                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\n",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\n",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\n",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\n",ans);
	}
	return 0;
}

 

标签:牛客,int,题解,top,mid,下雨天,while,水量,cr
来源: https://www.cnblogs.com/YoOXiii/p/12174081.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有