zoukankan      html  css  js  c++  java
  • luogu P4632 [APIO2018] New Home 新家 线段树 set 二分

    写了一种比较容易理解 但是常数很大的sol.

    容易发现可以扫描线。

    维护好序列之后发现很难查距离 考虑二分。

    这里二分可以在线段树上进行 当然可能存在一些问题 如果离散化的话需要处理一些比较麻烦的细节 如:需要向左二分一次向右二分一次什么的。

    当然也可以直接在外面二分 得到一个区间 发现只要区间的本质不同的颜色满足 K种判定即可成功。

    在线数颜色一般使用线段树+树状数组。不过这样做事log^3的。

    考虑判定区间的另外一种方式 我们只需要所有颜色在这个区间中即可。 可以发现 如果总颜色不足k种 就GG.

    发现某种颜色的最右端点小于当前区间 GG.这个东西可以开一个set维护。

    考虑之后所颜色都在这个区间加后缀的一段区间内 考虑后续的一段区间内的pre的最小值 如果在这个区间之外 那么GG 反之 可以发现所有颜色都在当前区间内。

    由于存在重复的值 考虑线段树上每个节点再开一个set维护重复的信息。

    这样总复杂度为(n+Q)log^2.

    常数较大 开o2后快很多。不过写的时候防止出错在插入一个点删除一个点的时候分类讨论了 写的有点麻烦。可以简单的将几个操作给合并。

    注意线段树空间开8倍。

    const int MAXN=300010;
    int n,Q,k,num,cnt,cc,sum;
    struct wy
    {
    	int l,r;
    	int sum;
    }t[MAXN<<3];
    struct jl
    {
    	int l,k;//时间
    	int x,op;//坐标,类型
    	inline int friend operator <(jl a,jl b){return a.l==b.l?a.k>b.k:a.l<b.l;}
    }s[MAXN<<2];
    int b[MAXN<<1],ans[MAXN];
    multiset<int>v[MAXN],ss;//存放每个颜色的位置集合 ss维护全局颜色的最右端点
    multiset<int>g[MAXN<<3];//线段树上每个节点下吊一个set维护pre的最小值 
    multiset<int>::iterator it,itt,tt,pp;
    inline void discrete()
    {
    	sort(b+1,b+1+num);
    	rep(1,num,i)if(i==1||b[i]!=b[i-1])b[++cnt]=b[i];
    }
    inline void build(int p,int l,int r)
    {
    	l(p)=l;r(p)=r;sum(p)=INF;
    	if(l==r)return;
    	int mid=(l+r)>>1;
    	build(zz,l,mid);
    	build(yy,mid+1,r);
    }
    inline void del(int p,int x,int w)
    {
    	if(l(p)==r(p))
    	{
    		pp=g[p].find(b[w]);
    		g[p].erase(pp);
    		if(!g[p].size())sum(p)=INF;
    		else sum(p)=*g[p].begin();
    		return;
    	}
    	int mid=(l(p)+r(p))>>1;
    	if(x<=mid)del(zz,x,w);
    	else del(yy,x,w);
    	sum(p)=min(sum(zz),sum(yy));
    }
    inline void change(int p,int x,int w)
    {
    	if(l(p)==r(p))
    	{
    		g[p].insert(b[w]);
    		sum(p)=*g[p].begin();
    		return;
    	}
    	int mid=(l(p)+r(p))>>1;
    	if(x<=mid)change(zz,x,w);
    	else change(yy,x,w);
    	sum(p)=min(sum(zz),sum(yy));
    }
    inline void modify(int p,int x,int w1,int w2)
    {
    	if(l(p)==r(p))
    	{
    		pp=g[p].find(b[w1]);
    		g[p].erase(pp);
    		g[p].insert(b[w2]);
    		sum(p)=*g[p].begin();
    		return;
    	}
    	int mid=(l(p)+r(p))>>1;
    	if(x<=mid)modify(zz,x,w1,w2);
    	else modify(yy,x,w1,w2);
    	sum(p)=min(sum(zz),sum(yy));
    }
    inline void del(int x,int op)//删除在x位置上类型为op的商店
    {
    	//更新全局右端点 更新线段树 更新sum 更新前驱
    	it=v[op].find(x);
    	if(it==v[op].begin())
    	{
    		del(1,x,0);
    		if(it==--v[op].end())//无前驱无后继
    		{
    			--sum;
    			itt=ss.find(b[x]);
    			ss.erase(itt);
    		}
    		else//无前驱有后继
    		{
    			itt=it;++itt;
    			modify(1,*itt,x,0);
    		}
    	}
    	else
    	{
    		itt=it;--itt;
    		del(1,x,*itt);
    		if(it==--v[op].end())//有前驱无后继
    		{
    			tt=ss.find(b[x]);
    			ss.erase(tt);
    			ss.insert(b[*itt]);
    		}
    		else//有前驱有后继
    		{
    			tt=it;++tt;
    			modify(1,*tt,x,*itt);
    		}
    	}
    	v[op].erase(it);
    }
    inline void insert(int x,int op)//插入
    {
    	//更新全局右端点 更新线段树 更新sum 更新前驱
    	v[op].insert(x);
    	it=v[op].find(x);
    	if(it==v[op].begin())
    	{
    		change(1,x,0);
    		if(it==--v[op].end())//无前驱无后继
    		{
    			++sum;ss.insert(b[x]);
    		}
    		else//无前驱有后继
    		{
    			itt=it;++itt;
    			modify(1,*itt,0,x);
    		}
    	}
    	else
    	{
    		itt=it;--itt;
    		change(1,x,*itt);
    		if(it==--v[op].end())//有前驱无后继
    		{
    			ss.insert(b[x]);
    			tt=ss.find(b[*itt]);
    			ss.erase(tt);
    		}
    		else//有前驱有后继
    		{
    			tt=it;++tt;
    			modify(1,*tt,*itt,x);
    		}
    	}
    }
    inline int ask(int p,int l,int r)
    {
    	if(l<=l(p)&&r>=r(p))return sum(p);
    	int mid=(l(p)+r(p))>>1;
    	int ww=INF;
    	if(l<=mid)ww=min(ww,ask(zz,l,r));
    	if(r>mid)ww=min(ww,ask(yy,l,r));
    	return ww;
    }
    inline int check(int x,int w)//查询 x+w+1~n 之中颜色的前驱最小值和 颜色右端点最小值是否大于等于x-mid
    {
    	int R=lower_bound(b+1,b+1+cnt,b[x]+w+1)-b;
    	int ww=R>cnt?INF:ask(1,R,cnt);
    	if(!ww)return 0;
    	ww=min(*ss.begin(),ww);
    	return ww>=b[x]-w;
    }
    inline void ask(int x,int id)//坐标 编号
    {
    	if(sum!=k){ans[id]=-1;return;}
    	int l=0,r=b[cnt];
    	while(l<r)
    	{
    		int mid=(l+r)>>1;
    		if(check(x,mid))r=mid;
    		else l=mid+1;
    	}
    	ans[id]=l;
    }
    int main()
    {
    	freopen("1.in","r",stdin);
    	get(n);get(k);get(Q);//k是类型 Q是询问.
    	rep(1,n,i)
    	{
    		++cc;
    		get(s[cc].x);get(s[cc].op);
    		get(s[cc].l);s[cc].k=1;
    		++cc;s[cc].x=s[cc-1].x;s[cc].op=s[cc-1].op;
    		get(s[cc].l);s[cc].k=-1;b[++num]=s[cc].x;
    	}
    	rep(1,Q,i)
    	{
    		get(s[++cc].x);get(s[cc].l);
    		s[cc].op=i;
    		b[++num]=s[cc].x;
    	}
    	discrete();
    	sort(s+1,s+1+cc);
    	build(1,1,cnt);
    	rep(1,cc,i)
    	{
    		s[i].x=lower_bound(b+1,b+1+cnt,s[i].x)-b;
    		if(!s[i].k)ask(s[i].x,s[i].op);
    		else
    		{
    			if(s[i].k==-1)del(s[i].x,s[i].op);
    			else insert(s[i].x,s[i].op);
    		}
    	}
    	rep(1,Q,i)put(ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    Django环境配置
    python pip使用报错: Fatal error in launcher: Unable to create process using '"c:python27python.exe" "C:Python27Scriptspip.exe" '
    我的第100篇博客
    测试代码(测试函数)
    python 存储数据
    python 异常之进阶操作
    从实战的角度谈微服务(三):基于Feign的熔断
    从实战的角度谈微服务(三):基于Feign服务间的调用
    从实战的角度谈微服务(二):服务注册与发现(Eureka服务端)
    从实战的角度谈微服务(一):初识微服务
  • 原文地址:https://www.cnblogs.com/chdy/p/12685039.html
Copyright © 2011-2022 走看看