zoukankan      html  css  js  c++  java
  • 题解 CF1136E 【Nastya Hasn't Written a Legend】

    推销 膜 zhouakngyang 宝典

    (color{black}{ exttt {z}}color{red}{ exttt {houakngyang}}) AK 完比赛让我来做这题,我做了1h,他1hAK,被他吊起来打。

    作为目前洛谷和CF的最优解来写篇题解

    题解

    靠的就是灵光一现。。。

    看完题,正常人都会 发现这个 (k_i) 怎么这么烦啊,好像啥都不能维护啊。修改到哪里啊,怎么没有单调性啊。

    然后不死心,开始找各种数据结构来维护。time is passing,rank is falling......

    注意到这个数列看起来有点可以二分的感觉,但是没有单调性。

    灵光一现:不好维护就别维护呗,用前缀和抵消啊

    (s_n=sumlimits_{i=2}^{n}k_i) (注意,k的下标为了方便从 (2) 开始)

    (b_i=a_i-s_i)

    那么 (a_{i+1}<a_i+k_i Leftrightarrow b_{i+1}<b_i)

    所以 (b) 单调不降,否则就更新过去了

    于是可以二分右端点,二分到最靠右(事实上这与题目有些出入,但是带个等号没影响)的满足 (b_{i+1} le b_i+x)(x) 是加上的数)的位置 (r) ,在线段树上把 ([i,r]) 内的数赋值为 (b_i+k) ,最后询问的时候输出:([l,r]) 的区间和加上 (ss_r-ss_{l-1})(ss_i=sumlimits_{j=2}^{i}s_j) (就是把减掉的前缀和加回去)

    优化

    最优解不可能只用上面的思路的啊。

    上面那个东西二分套个线段树是 (O(nlog^2n)) 的。

    非常显然的可以把线段树二分换成单只 (log) 的线段树上查找,因为线段树本身就是二分数据结构。

    具体来说,对于每个区间维护最小值。

    • 如果右区间的最小值小于要二分的 (v) ,那么往右区间走,因为一定更靠右切存在可行解
    • 否则往左区间走。

    于是这道题就变成 (O(nlog n)) 的啦。

    然后发现我是次优解。

    忽然有了信心,加个火车头加个fread就最优解了。

    #define int long long
    #define N 100009
    const int inf=1e18;
    inline int rd() {
    	int x=0,f=1;char ch=getchar();
    	while(!isdigit(ch)) {if(ch=='-')f=-1;ch=getchar();}
    	while(isdigit(ch))x=x*10+(ch^48),ch=getchar();
    	return x*f;
    }
    inline int rdc() {
    	char ch=getchar();
    	while(ch!='+'&&ch!='s')ch=getchar();
    	return ch=='s';
    }
    int a[N],k[N],s[N],v[N],n,ss[N];
    int val[N<<2],tag[N<<2],miv[N<<2];
    #define lc (p<<1)
    #define rc (p<<1|1)
    void pushup(int p) {
    	val[p]=val[lc]+val[rc],miv[p]=std::min(miv[lc],miv[rc]);
    }
    void build(int l,int r,int p) {
    	tag[p]=inf;
    	if(l==r)return val[p]=miv[p]=v[l],void();
    	int mid=(l+r)>>1;
    	build(l,mid,lc),build(mid+1,r,rc);
    	pushup(p);
    }
    void pushdown(int p,int l,int r) {
    	if(tag[p]==inf)return;
    	int mid=(l+r)>>1;
    	tag[lc]=tag[rc]=tag[p],
    	miv[lc]=miv[rc]=tag[p],
    	val[lc]=tag[p]*(mid-l+1),
    	val[rc]=tag[p]*(r-mid),
    	tag[p]=inf;
    }
    int query(int ql,int qr,int l,int r,int p) {
    	if(ql<=l&&r<=qr)return val[p];
    	pushdown(p,l,r);
    	int mid=(l+r)>>1,res=0;
    	if(ql<=mid)res+=query(ql,qr,l,mid,lc);
    	if(mid<qr)res+=query(ql,qr,mid+1,r,rc);
    	return res;
    }
    void update(int ql,int qr,int l,int r,int p,int k) {
    	if(ql<=l&&r<=qr) {
    		tag[p]=miv[p]=k,val[p]=k*(r-l+1);return;
    	}
    	pushdown(p,l,r);
    	int mid=(l+r)>>1;
    	if(ql<=mid)update(ql,qr,l,mid,lc,k);
    	if(mid<qr)update(ql,qr,mid+1,r,rc,k);
    	pushup(p);
    }
    int ask(int v,int l,int r,int p) {
    	if(l==r)return l;
    	pushdown(p,l,r);
    	int mid=(l+r)>>1;
    	if(miv[rc]<=v)return ask(v,mid+1,r,rc);
    	else return ask(v,l,mid,lc);
    }
    signed main() {
    	n=rd();
    	for(int i=1;i<=n;++i)a[i]=rd();
    	for(int i=2;i<=n;++i)k[i]=rd(),s[i]=s[i-1]+k[i],ss[i]=ss[i-1]+s[i];
    	for(int i=1;i<=n;++i)v[i]=a[i]-s[i];
    	build(1,n,1);
    	for(int q=rd();q;--q) {
    		int opt=rdc(),x=rd(),y=rd();
    		if(opt)printf("%lld
    ",query(x,y,1,n,1)+ss[y]-ss[x-1]);
    		else {
    			int now=query(x,x,1,n,1);
    			update(x,ask(now+y,1,n,1),1,n,1,now+y);
    		}
    	}
    	return 0;
    }
    
    路漫漫其修远兮,吾将上下而求索
  • 相关阅读:
    HDU 4628 Pieces
    HDU 2983 Integer Transmission
    HDU 1820 Little Bishops
    CodeForces 165E Compatible Numbers
    CodeForces 11D A Simple Task
    HDU 4804 Campus Design
    HDU 3182 Hamburger Magi
    Linux的用户和组
    Linux文件/目录权限及归属
    Vim使用介绍
  • 原文地址:https://www.cnblogs.com/zzctommy/p/13634533.html
Copyright © 2011-2022 走看看