zoukankan      html  css  js  c++  java
  • [Ynoi2015]纵使日薄西山

    题目大意:

    给定一个序列,每次单点修改,然后进行询问。

    定义一次操作为,选择一个位置$x$,将这个位置的数和左边、右边两个位置的数(不存在则忽略)各减去1,然后和0取max。

    对序列中最大的位置进行一次操作(相同则取最前面的),不断重复,直到所有位置为0为止。

    问执行了多少次操作。

    询问互相独立(即下一次询问的序列并不是全0)。

    解题思路:

    在太阳西斜的这个世界里,置身天上之森。等这场战争结束之后,不归之人与望眼欲穿的众人, 人人本着正义之名,长存不灭的过去、逐渐消逝的未来。我回来了,纵使日薄西山,即便看不到未来,此时此刻的光辉,盼君勿忘。————世界上最幸福的女孩

    我的愿望是,和珂朵莉一样可爱。


    淦不动辣QaQ就窝一个在那瞎分块

    一开始的做法是先分块,求出块内的答案,然后瞎分类讨论(自闭了QAQ)。

    实际上这个性质挺优美的为啥窝就是发现不了啊

    显然一次对某个位置操作到底,和答案是一样的(当你选择这个数进行操作后,其左边和右边就永远不会被操作。而其他数对这个位置无影响)。

    所以相当于求所有会被操作的位置上的数的和。

    考虑一个单调递增或单调递减的序列,在这上面选的数一定是一个选一个不选(奇偶性相同)。

    所以我们对奇数、偶数位置分别用树状数组维护一下,然后用set记录序列的极大、极小值的位置。则可以快速计算出两个极值之间的区间的贡献。

    然后对于单点修改,对受到影响的部分区间重新计算贡献即可。

    细节挺多的,比如对极小值是否取到的考虑(仅当其左右两个极大值的位置的奇偶性和这个极小值位置相同时,才能取到这个值)。

    时间复杂度$O(nlog n)$。

    C++ Code:

    #include<cstdio>
    #include<set>
    const int N=1e5+6;
    typedef long long LL;
    int n,a[N];
    struct BIT{
    	LL b[N];
    	inline void add(int i,int x){for(;i<N;i+=i&-i)b[i]+=x;}
    	inline LL ask(int i){LL x=0;for(;i;i^=i&-i)x+=b[i];return x;}
    }odd,even;
    std::set<int>s;
    LL ans=0;
    LL get(int x){
    	if(x<1||x>n)return 0;
    	auto it=s.find(x);
    	if(*it==1){
    		auto nxt=it;++nxt;
    		if(a[*it]<a[*nxt]){
    			if(*nxt&1)return odd.ask(*nxt-1)-odd.ask(*it-1);else
    				return even.ask(*nxt-1)-even.ask(*it-1);
    		}else return 0;
    	}
    	if(*it==n){
    		auto pre=it;--pre;
    		if(a[*pre]>=a[*it]){
    			if(*pre&1)return odd.ask(*it)-odd.ask(*pre-1);else
    				return even.ask(*it)-even.ask(*pre-1);
    		}else return a[n];
    	}
    	auto pre=it,nxt=it;--pre,++nxt;
    	if(a[*pre]>=a[*it]&&a[*it]<a[*nxt]){
    		LL ret=0;
    		if(*pre&1)ret+=odd.ask(*it-1)-odd.ask(*pre-1);else
    			ret+=even.ask(*it-1)-even.ask(*pre-1);
    		if(*nxt&1)ret+=odd.ask(*nxt-1)-odd.ask(*it);else
    			ret+=even.ask(*nxt-1)-even.ask(*it);
    		if((*pre&1)==(*it&1)&&(*nxt&1)==(*it&1))ret+=a[*it];
    		return ret;
    	}else return 0;
    }
    int main(){
    	scanf("%d",&n);
    	for(int i=1;i<=n;++i)
    		scanf("%d",a+i),((i&1)?odd:even).add(i,a[i]);
    	s.insert(-2),s.insert(-1),s.insert(n+2),s.insert(n+3);
    	s.insert(1);
    	for(int i=2;i<n;++i)
    		if(a[i-1]<a[i]&&a[i]>=a[i+1]||a[i-1]>=a[i]&&a[i]<a[i+1])s.insert(i);
    	s.insert(n);
    	for(int i:s)
    		ans+=get(i);
    	int q;
    	for(scanf("%d",&q);q--;){
    		int x,y;
    		scanf("%d%d",&x,&y);
    		int nxt=*s.upper_bound(x),pre=*--s.lower_bound(x);
    		ans-=get(nxt),ans-=get(pre);
    		nxt=*s.upper_bound(nxt),pre=*--s.lower_bound(pre);
    		ans-=get(nxt),ans-=get(pre);
    		if(s.count(x))ans-=get(x),s.erase(x);
    		s.erase(x-1),s.erase(x+1);
    		if(x&1)odd.add(x,-a[x]),odd.add(x,y);else
    			even.add(x,-a[x]),even.add(x,y);
    		a[x]=y; 
    		for(int i=x-1;i<=x+1;++i){
    			if(i<1||i>n)continue;
    			if(i==1||i==n)s.insert(i);else
    				if(a[i-1]<a[i]&&a[i]>=a[i+1]||a[i-1]>=a[i]&&a[i]<a[i+1])s.insert(i);
    		}
    		for(auto l=s.find(pre),r=s.upper_bound(nxt);l!=r;++l)
    			ans+=get(*l);
    		printf("%lld
    ",ans);
    	}
    	return 0;
    }
  • 相关阅读:
    html5+css3中的background: -moz-linear-gradient 用法 (转载)
    CentOS 安装Apache服务
    Linux 笔记
    CURL 笔记
    Spring Application Context文件没有提示功能解决方法
    LeetCode 389. Find the Difference
    LeetCode 104. Maximum Depth of Binary Tree
    LeetCode 520. Detect Capital
    LeetCode 448. Find All Numbers Disappeared in an Array
    LeetCode 136. Single Number
  • 原文地址:https://www.cnblogs.com/Mrsrz/p/10602966.html
Copyright © 2011-2022 走看看