zoukankan      html  css  js  c++  java
  • CF::Gym 100960G

    CF::Gym题目页面传送门

    给定一个数列(a),支持(q)次操作:令(a_x=y),并设(a')(a)从大到小排序的结果,查询满足该位置上的数大于等于其后所有数的和的位置数量。

    (ninleft[1,10^5 ight],qinleft[1,5 imes10^4 ight],a_iinleft[1,10^{12} ight])

    考虑设(suM_i)(a')在位置(i)处的后缀和,那么位置(i)满足条件显然当且仅当(a'_i-suM_{i+1}geq0)。不难想到维护(a'_i-suM_{i+1})

    肯定是要按(a_i)从大往小排列的。注意到每次修改,可能会让(a_x)(a')中移个位置,而其他元素的相对位置不变。设(a_x)本来在(a')中位置为(p),修改完跑到了(p')。那么分(p<p')(pgeq p')两种情况。这里以前者为例,后者类似。

    显然整个(a')序列分成三段:

    1. (1sim p),这一段的(a'_i-suM_{i+1})值显然都要加上(a_x-y)
    2. (psim p'),这一段的(a'_i-suM_{i+1})值显然都要加上(-y)
    3. (p'sim n),这一段的(a'_i-suM_{i+1})值显然不变。

    看到区间增加,不难想到线段树配合懒标记。那么问题来了,维护啥呢?咋查询呢?线段树套平衡树肯定是不行的,因为是区间修改。线段树直接维护也维护不动。考虑让线段树起到剪枝的作用:每个节点维护当前区间的(a'_i-suM_{i+1})最大值(这个显然是懒标记可做的)。查询的时候从根往下走,对于每个儿子,如果它的最大值(geq0)则往下走,否则不往下走(即里面不可能有符合要求的位置)。

    这样复杂度是多少呢?注意到一个非常重要的性质:答案是(mathrm O(log v))级别的,其中(v)(a)的值域大小。证明非常简单,大概就是每有一个答案,后缀和就要增一倍,并且当前位置的数要大于等于后缀和。这样一来,每个符合要求的数就会有一条对应的线段树上的根到叶子的链,多条链的并集是被经过的节点集合,(mathrm O(log nlog v))

    考虑到还要插入与删除,想用线段树的话要离线预留好位置,比较烦我懒得写了。其他的方法有:动态开点线段树,(mathrm O!left(log^2v ight));平衡树,复杂度不变,分析差不多。我写了后者,使用fhq-Treap。插入删除直接转化成修改,就不需要垃圾桶了。

    时间复杂度(mathrm O(nlog n+qlog nlog v))

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    #define int long long
    #define mp make_pair
    #define X first
    #define Y second
    const int inf=0x3f3f3f3f3f3f3f3f;
    mt19937 rng(20060617);
    const int N=100000;
    int n,qu;
    int a[N+1];
    int b[N+1],suM[N+2];
    struct fhq_treap{ 
    	int sz,root;
    	struct node{unsigned key;int lson,rson,sz,v,dif,sum,mx,lz;}nd[N+1];
    	#define key(p) nd[p].key
    	#define lson(p) nd[p].lson
    	#define rson(p) nd[p].rson
    	#define sz(p) nd[p].sz
    	#define v(p) nd[p].v
    	#define dif(p) nd[p].dif
    	#define sum(p) nd[p].sum
    	#define mx(p) nd[p].mx
    	#define lz(p) nd[p].lz
    	int bld(int l=1,int r=n){
    		int mid=l+r>>1,p=nwnd(b[mid],b[mid]-suM[mid+1]);
    		if(l<mid)lson(p)=bld(l,mid-1);
    		if(r>mid)rson(p)=bld(mid+1,r);
    		return sprup(p),p;
    	}
    	void init(){
    		sz=0;
    		nd[0]=node({0,0,0,0,0,-inf,0,-inf,0});
    		root=bld();
    	}
    	void sprup(int p){
    		sum(p)=sum(lson(p))+v(p)+sum(rson(p));
    		mx(p)=max(mx(lson(p)),max(dif(p),mx(rson(p))));
    		sz(p)=sz(lson(p))+1+sz(rson(p));
    	}
    	void sprdwn(int p){
    		if(lz(p)){
    			tag(lson(p),lz(p));tag(rson(p),lz(p));
    			lz(p)=0;
    		}
    	}
    	void tag(int p,int v){
    		if(p)dif(p)+=v,mx(p)+=v,lz(p)+=v;
    	}
    	pair<int,int> split(int x,int p=-1){~p||(p=root);
    		if(!x)return mp(0,p);
    		sprdwn(p);
    		pair<int,int> sp;
    		if(x<=sz(lson(p)))return sp=split(x,lson(p)),lson(p)=sp.Y,sprup(p),mp(sp.X,p);
    		return sp=split(x-1-sz(lson(p)),rson(p)),rson(p)=sp.X,sprup(p),mp(p,sp.Y);
    	}
    	int mrg(int p,int q){
    		if(!p||!q)return p|q;
    		sprdwn(p);sprdwn(q);
    		if(key(p)<key(q))return rson(p)=mrg(rson(p),q),sprup(p),p;
    		return lson(q)=mrg(p,lson(q)),sprup(q),q;
    	}
    	int grt(int v,int p=-1){~p||(p=root);
    		if(!p)return 0;
    		sprdwn(p);
    		if(v(p)>v)return sz(lson(p))+1+grt(v,rson(p));
    		return grt(v,lson(p));
    	}
    	int nwnd(int v,int dif){
    		return nd[++sz]=node({rng(),0,0,1,v,dif,v,dif,0}),sz;
    	}
    	void mv_rit(int v1,int v2){
    		pair<int,int> sp=split(grt(v1)),sp0=split(1,sp.Y),sp1=split(grt(v2,sp0.Y),sp0.Y);
    		//sp.X,del(sp0.X),sp1.X,insert(v2),sp1.Y
    		v(sp0.X)=sum(sp0.X)=v2,dif(sp0.X)=mx(sp0.X)=v2-sum(sp1.Y);
    		tag(sp.X,v1-v2);tag(sp1.X,-v2);
    		root=mrg(sp.X,mrg(sp1.X,mrg(sp0.X,sp1.Y)));
    	}
    	void mv_lft(int v1,int v2){
    		pair<int,int> sp=split(grt(v2)),sp0=split(grt(v1,sp.Y),sp.Y),sp1=split(1,sp0.Y);
    		//sp.X,insert(v2),sp0.X,del(sp1.X),sp1.Y
    		v(sp1.X)=sum(sp1.X)=v2,dif(sp1.X)=mx(sp1.X)=v2-sum(sp0.X)-sum(sp1.Y);
    		tag(sp.X,v1-v2);tag(sp0.X,v1);
    		root=mrg(sp.X,mrg(sp1.X,mrg(sp0.X,sp1.Y)));
    	}
    	int cnt(int p=-1){~p||(p=root);
    		if(!p)return 0;
    		sprdwn(p);
    		int res=0;
    		if(dif(p)>=0)res++;
    		if(mx(lson(p))>=0)res+=cnt(lson(p));
    		if(mx(rson(p))>=0)res+=cnt(rson(p));
    		return res;
    	}
    	void dfs(int p=-1){~p||(p=root);
    		if(!p)return;
    		sprdwn(p);
    		dfs(lson(p));
    		cout<<v(p)<<" "<<dif(p)<<"!
    ";
    		dfs(rson(p));
    	}
    }trp;
    signed main(){
    	cin>>n;
    	for(int i=1;i<=n;i++)scanf("%lld",a+i),b[i]=a[i];
    	sort(b+1,b+n+1,greater<int>());
    	for(int i=n;i;i--)suM[i]=suM[i+1]+b[i];
    	trp.init();
    //	trp.dfs();
    	cout<<trp.cnt()<<"
    ";
    	cin>>qu;
    	while(qu--){
    		int x,y;
    		scanf("%lld%lld",&x,&y);
    //		cout<<a[x]<<" "<<y<<"!!
    ";
    		if(a[x]>y)trp.mv_rit(a[x],y);
    		else trp.mv_lft(a[x],y);
    		a[x]=y;
    //		trp.dfs();
    		printf("%lld
    ",trp.cnt());
    	}
    	return 0;
    }
    

    后来yxh告诉我了另一个神仙方法,是用二进制搞的,代码特别短。当时乍一看是1log的,woc这么强的吗???还准备学习一下。现在才发现也是2log的,复杂度跟上述做法一样,就懒得研究了。

    总体来说是比较简单的一题。

  • 相关阅读:
    转义字符:html、mysql、postgresql、json、php
    php_mysql、php_mysqli 与 pdo_mysql 的区别与选择
    一件小事测试各个搜索引擎:谷歌、bing、有道、百度、搜狗、360
    一键安装lnmp:自动检测最新稳定版、无需root权限
    apache graceful 与 cpu占用率
    互联网创业的准备——版本控制与上线
    201212互联网创意、创业项目整理
    【图】游东天目山
    那些在11gR2中可能惹祸的新特性,一张列表帮助你摆脱升级11gR2带来的烦恼
    【转】卡巴斯基安全公告称甲骨文数据库存在加密漏洞
  • 原文地址:https://www.cnblogs.com/ycx-akioi/p/CF-Gym-100960G.html
Copyright © 2011-2022 走看看