Gym 100490E-E - Environment Problems- 伸展树/平衡树/离散化点

题意:n次操作

操作1给区间【a,b】加上1

操作2查询某个点x的值

。。。。这不就是线段树的区间更新单点操作嘛。。。。。然后1<=a,b<= 10^9...也就是不能线段树直接来了

思路: 把每次输入的l 存到一个有序集合,每次输入的r存到一个有序集合

然后对于每次查询的点X。我们只需要看大于该点X的l有多少个,大于等于该点X的r有多少个,然后用r的个数减去l的个数。

就得到该点被覆盖的次数了...也就是该点的值

实现:

1、每次插入完,储存数据的结构必须是有序的(这里我用set.logn..)

2、每次查询的时候,用二分在有序结构中分别找到大于x的l和r (二分logn)

3、对于得到的大于X的l,和r,  还得知道他们在该有序结构中的排名...才能用总数-排名得到 大于X的个数...这里我用treap logn查询某个数的排名 


复杂度就是 (n*logn)..


然而其实对 步骤 1 2 3其实直接用一个平衡树或者伸展树实现就好了....我等渣渣只好用set+treap狡猾地实现了。。。

代码在最后

----------------------------------------------------------------------------

另外一种做法是离线做法,对 点离散化  (比第一种稍快)

先把所有的操作输入储存,把所有查询的点 先离散化 排好序,(nlogn)

然后开始按输入顺序 执行

 操作1:  每次给区间 【a,b】加1的时候,先在 <离散化+排序好的> 查询点 的数组中二分找到 第一个大于等于a的下标X和第一个大于b的下标Y 然后用树状数组 给 【x,y】加1(logn)  

操作2:由于操作1已经实现了,直接在树状数组总取 查询点对应离散化后的点的值即可


set+treap代码:

//treap
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <iostream>
#include <queue>
#include <map>
#include <set>
#include <vector>
using namespace std; 
#include <iostream>
using namespace std; 
#define MAXN 200010
struct data{
    int l,r,v,size,rnd,w;
};
 
class tp
{
public: 
    int n,size,root,ans;
    tp()
    {
        n=size=root=ans=0;
    }
    data tr[MAXN];
    void update(int k)//更新结点信息
    {
        tr[k].size=tr[tr[k].l].size+tr[tr[k].r].size+tr[k].w;
    }
    void rturn(int &k)
    {
        int t=tr[k].l;tr[k].l=tr[t].r;tr[t].r=k;
        tr[t].size=tr[k].size;update(k);k=t;
    }
    void lturn(int &k)
    {
        int t=tr[k].r;tr[k].r=tr[t].l;tr[t].l=k;
        tr[t].size=tr[k].size;update(k);k=t;
    }
    void insert(int &k,int x)
    {
        if(k==0)
        {
            size++;k=size;
            tr[k].size=tr[k].w=1;tr[k].v=x;tr[k].rnd=rand();
            return;
        }
        tr[k].size++;
        if(tr[k].v==x)tr[k].w++;//每个结点顺便记录下与该节点相同值的数的个数
        else if(x>tr[k].v)
        {
            insert(tr[k].r,x);
            if(tr[tr[k].r].rnd<tr[k].rnd)lturn(k);//维护堆性质
        }
        else 
        {
            insert(tr[k].l,x);
            if(tr[tr[k].l].rnd<tr[k].rnd)rturn(k);
        } 
    }
    void del(int &k,int x)
    {
        if(k==0)return; 
        if(tr[k].v==x)
        {
            if(tr[k].w>1)
            {
                tr[k].w--;tr[k].size--;return;//若不止相同值的个数有多个,删去一个
            }
            if(tr[k].l*tr[k].r==0)k=tr[k].l+tr[k].r;//有一个儿子为空
            else if(tr[tr[k].l].rnd<tr[tr[k].r].rnd)
                rturn(k),del(k,x);
            else lturn(k),del(k,x);
        }
        else if(x>tr[k].v)
            tr[k].size--,del(tr[k].r,x);
        else tr[k].size--,del(tr[k].l,x);
    }
    int query_rank(int k,int x)
    {
        if(k==0)return 0;
        if(tr[k].v==x)return tr[tr[k].l].size+1;
        else if(x>tr[k].v)
            return tr[tr[k].l].size+tr[k].w+query_rank(tr[k].r,x);
        else return query_rank(tr[k].l,x);
    }
    int query_num(int k,int x)
    {
        if(k==0)return 0;
        if(x<=tr[tr[k].l].size)
            return query_num(tr[k].l,x);
        else if(x>tr[tr[k].l].size+tr[k].w)
            return query_num(tr[k].r,x-tr[tr[k].l].size-tr[k].w);
        else return tr[k].v;
    }
    void query_pro(int k,int x)
    {
        if(k==0)return;
        if(tr[k].v<x)
        {
            ans=k;query_pro(tr[k].r,x);
        }
        else query_pro(tr[k].l,x);
    }
    void query_sub(int k,int x)
    {
        if(k==0)return;
        if(tr[k].v>x)
        {
            ans=k;query_sub(tr[k].l,x);
        }
        else query_sub(tr[k].r,x);
    }
}; 

    tp sp1,sp2;
multiset<int> q1,q2;
multiset<int>::iterator t1,t2;
int main()
{ 
    freopen( "environment.in","r",stdin );  //scanf 从1.txt输入
    freopen( "environment.out","w",stdout );  //printf输出到1.tx
    int n,i,op;
    int aa,bb;
    scanf("%d", &n); 
    int root;
    int d=0;
    int ddd=1;
    int cnt=0;
    for(  i = 1; i <= n; i++) 
    {
        scanf("%d",&op);
        if (op==1)
        {
            scanf("%d%d",&aa,&bb);
            aa=aa+d;
            bb=bb+d;
            sp1.insert(sp1.root,aa);
            sp2.insert(sp2.root,bb);
            q1.insert(aa); 
               q2.insert(bb); 
            cnt++;
            
            
        }
        else
        { 

             int t3,t4;
               scanf("%d",&aa);
            t1=q1.upper_bound(aa);
            t2=q2.lower_bound(aa);
            if (t1==q1.end()) 
                t3=cnt+1;
            else
                t3=sp1.query_rank(sp1.root,*t1);
            if (t2==q2.end())
                t4=cnt+1;
            else
                t4=sp2.query_rank(sp2.root,*t2);
 
            d=(1+cnt-t4)- (1+cnt-t3);
            printf("%d\n",d);
            
            
            
            
            
        }
    }
    
    
    return 0;
}



离散化做法 :


 
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <iostream>
#include <queue>
#include <map>
#include <set>
#include <vector>
using namespace std; 
#include <iostream>
using namespace std; 
#define MAXN 200010
int n;

int tree[MAXN];
 struct node
{
	int x,num;		//把原始数据 的值 与原始序号储存
	bool operator <(const node& b )const
	{return x<b.x;}
	bool operator >(const node& b )const
	{return x>b.x;}
 		bool operator == (const node& b )const
	{return x==b.x;}
	node(){}
	node(int i,int k){x=i;num=k;}

};
int cmp(node a,node b)
{
	return a.x<b.x;			//把原始数据按值排序,
}


int max(int a,int b )
{
	if (a<b)return b;return a;
}

inline  int lowbit(int x)
{
	return x&-x;
}
void add(int x,int value)
{
	for (int i=x;i<=n;i=i+lowbit(i))
	{
		tree[i]+=value;
	}
}
int get(int x)
{
	int sum=0;
	for (int i=x;i;i-=lowbit(i))
	{
		sum+=tree[i];
	}
	return sum;
}
struct op
{
	int p;
	int a,b;

};
op ope[MAXN]; 
node  point[MAXN]; 
set<int> vis;
int main()
{
 	freopen( "environment.in","r",stdin );  //scanf 从1.txt输入
   freopen( "environment.out","w",stdout );  //printf输出到1.tx
 	int i,aa,bb,j;
	int op;
	scanf("%d",&n);
	int cnt=0;
	for (i=1;i<=n;i++)
	{
	 
		  scanf("%d",&op);
        if (op==1)
        {
            scanf("%d%d",&aa,&bb);
			ope[i].p=1;
			ope[i].a=aa;
			ope[i].b=bb;
			
        }
		else
		{
			scanf("%d",&aa);
			ope[i].p=2;
			ope[i].a=aa;
			if (vis.find(aa)!=vis.end()) continue;
			vis.insert(aa);
			point[++cnt].x=aa;
			point[cnt].num=cnt; 
		
		} 
	}
	sort(point+1,point+1+cnt,cmp);				//对原始数据按值排升序
  
	int d=0;
	for (i=1;i<=n;i++)
	{
		if (ope[i].p==1)
		{
			int l=d+ope[i].a;
			int r=d+ope[i].b;
			int lp=lower_bound(point+1,point+1+cnt,node(l,1))-point;
			int rp=upper_bound(point+1,point+1+cnt,node(r,1))-point;
			add(lp,1);
			add(rp,-1);


		}
		else
		{
			int position=lower_bound(point+1,point+cnt+1,node(ope[i].a,1))-point;
		 

			printf("%d\n",d=get(position));

		}
	}



  
    
    
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值