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的,复杂度跟上述做法一样,就懒得研究了。

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

  • 相关阅读:
    Security headers quick reference Learn more about headers that can keep your site safe and quickly look up the most important details.
    Missing dollar riddle
    Where Did the Other Dollar Go, Jeff?
    proteus 与 keil 联调
    cisco router nat
    router dhcp and dns listen
    配置802.1x在交换机的端口验证设置
    ASAv931安装&初始化及ASDM管理
    S5700与Cisco ACS做802.1x认证
    playwright
  • 原文地址:https://www.cnblogs.com/ycx-akioi/p/CF-Gym-100960G.html
Copyright © 2011-2022 走看看