ICode9

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

【CF643G】Choosing Ads(线段树)

2021-04-01 07:33:32  阅读:222  来源: 互联网

标签:lfloor Ads 血量 CF643G Choosing 区间 100 Data define


点此看题面

  • 给定一个长度为\(n\)的序列和一个整数\(p\)。
  • 要求支持两种操作,区间赋值;询问区间中出现次数至少占\(p\%\)的数,可以输出至多\(\lfloor\frac{100}p\rfloor\)个数,可以包含错解,但必须有正确的解。
  • \(n\le1.5\times10^5,20\le p\le100\)

前置:一个经典的简单题

一个很简单的问题,给定一个序列,求出其中出现超过一半的数。(要求不开数组)

这时候我们就考虑,用这个数的出现次数减去其余所有数的出现次数是为正的。

因此,我们可以把它看成一种格斗,确定一个数,血量就是它当前的出现次数。

然后每加入一个数,如果和这个数一样就将血量加\(1\),如果不一样就将血量减\(1\)。

当血量减至\(0\)的时候这个数就死了,我们就需要把它换成一个新数。

(说实话,这道题应该是我刚上\(BZOJ\)时做的第一道题,至今都给我留有很深的印象,这个套路我也一直都很想再度使用,今天终于有了机会。)

扩展:区间的合并

现在我们把上面这个问题扩展一下,想要用线段树来维护这个格斗过程,实现区间查询。

然后我们发现,实际上完全可以让两个区间分别格斗出一个数来,如果这两个数相同就合并血量,否则保留血量大的并将它的血量减去小的血量。

扩展:多个数的格斗

回到这道题,相当于我们现在已经解决了\(p\ge 51\)时的部分。(区间赋值显然就把这个区间格斗出的数设为赋值的数,血量设为区间长度即可)

而当\(p\)很小的时候,可能有多个数,其实不妨,只要把这\(\lfloor\frac {100}p\rfloor\)个数维护下来即可。

具体地,就是用右区间格斗出的每个数去打左区间格斗出的每个数。

如果有相同直接合并血量,如果左区间的数还未到\(\lfloor\frac{100}p\rfloor\)个直接加入。

否则找到左区间中所有数和这个数中血量最少的那个,把其余数血量都减少它的血量,再把这个血量最少的数删去。

尽管这样的合并是\(\lfloor\frac{100}p\rfloor^2\)的,但毕竟\(\lfloor\frac{100}p\rfloor\le5\),随便过。

代码:\(O(nlogn\lfloor\frac{100}p\rfloor^2)\)

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 150000
#define K 5
using namespace std;
int n,p,a[N+5];struct Data
{
	int k,V[K],C[K];I Data() {k=0,memset(V,0,sizeof(V)),memset(C,0,sizeof(C));}
	I friend Data operator + (Data x,Data y)//合并
	{
		for(RI i=0,j;i^y.k;++i)//枚举右区间每个数去打左区间
		{
			for(j=0;j^x.k&&x.V[j]^y.V[i];++j);if(j^x.k) {x.C[j]+=y.C[i];continue;}//如果存在相同直接合并血量
			if(x.k^(100/p)) {x.V[x.k]=y.V[i],x.C[x.k++]=y.C[i];continue;}//如果左区间未满直接加入
			for(j=0;j^x.k;++j) x.C[j]<y.C[i]&&(swap(x.V[j],y.V[i]),swap(x.C[j],y.C[i]),0);//找到血量最小的
			for(j=0;j^x.k;++j) x.C[j]-=y.C[i];//把其他数的血量全减去该数血量
		}return x;
	}
};
namespace FastIO
{
	#define FS 100000
	#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
	#define pc(c) (FC==FE&&(clear(),0),*FC++=c)
	int OT;char oc,FI[FS],FO[FS],OS[FS],*FA=FI,*FB=FI,*FC=FO,*FE=FO+FS;
	I void clear() {fwrite(FO,1,FC-FO,stdout),FC=FO;}
	Tp I void read(Ty& x) {x=0;W(!isdigit(oc=tc()));W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));}
	Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
	Tp I void write(Ty x) {W(OS[++OT]=x%10+48,x/=10);W(OT) pc(OS[OT--]);}
	I void print(Con Data& v) {write(v.k),pc(' ');for(RI i=0;i^v.k;++i) write(v.V[i]),pc(' ');pc('\n');}
}using namespace FastIO;
class SegmentTree
{
	private:
		#define PT CI l=1,CI r=n,CI rt=1
		#define LT l,mid,rt<<1
		#define RT mid+1,r,rt<<1|1
		#define PU() (V[rt]=V[rt<<1]+V[rt<<1|1])
		#define PD() (F[rt]&&(T(rt<<1,F[rt],mid-l+1),T(rt<<1|1,F[rt],r-mid),F[rt]=0))
		#define T(x,v,c) (V[x].k=1,V[x].V[0]=F[x]=v,V[x].C[0]=c)//格斗出的数设为v,血量设为区间长度c
		Data V[N<<2];int F[N<<2];
	public:
		I void B(PT) {if(l==r) return (void)T(rt,a[l],1);RI mid=l+r>>1;B(LT),B(RT),PU();}
		I void U(CI L,CI R,CI v,PT)//区间赋值
		{
			if(L<=l&&r<=R) return (void)T(rt,v,r-l+1);RI mid=l+r>>1;PD();
			L<=mid&&(U(L,R,v,LT),0),R>mid&&(U(L,R,v,RT),0),PU();
		}
		I Data Q(CI L,CI R,PT)//区间询问
		{
			if(L==l&&r==R) return V[rt];RI mid=l+r>>1;PD();if(R<=mid) return Q(L,R,LT);
			if(L>mid) return Q(L,R,RT);return Q(L,mid,LT)+Q(mid+1,R,RT);
		}
}S;
int main()
{
	RI Qt,i,op,x,y,v;for(read(n,Qt,p),i=1;i<=n;++i) read(a[i]);S.B();
	W(Qt--) read(op,x,y),op==1?(read(v),S.U(x,y,v)):print(S.Q(x,y));return clear(),0;
}

标签:lfloor,Ads,血量,CF643G,Choosing,区间,100,Data,define
来源: https://www.cnblogs.com/chenxiaoran666/p/CF643G.html

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

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

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

ICode9版权所有