• CSU-ACM集训-模板-主席树


    原hdu 2665 Kth number

    题意

    (n)个数和(m)次查询,每个查询包含区间([x,y]),求区间内第(K)大的数

    思路

    可持久化线段树,即主席树,第一次建立一个空的线段树,使用(root)下标表示访问第几次时间,数据离散化后。注意下标从1开始。
    注意(cnt)可能是乱序的,但是(root)控制时间区间,即表示时间(i)的根节点为(nodes[root[i]])(nodes)(l)(r)控制树的左右关系。

    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <cmath>
    #include <cctype>
    #include <iostream>
    #include <algorithm>
    #include <string>
    #include <vector>
    #include <queue>
    #include <map>
    #include <set>
    #include <sstream>
    #include<iomanip>
    #define FOR(i,a,b) for(int i=a;i<b;i++)
    #define FOR2(i,a,b) for(int i=a;i<=b;i++)
    #define sync ios::sync_with_stdio(false);cin.tie(0) 
    #define ll long long
    #define MAXN 100010
    using namespace std;
    typedef struct{
    	int l,r,sum;
    }NODE;NODE nodes[20*MAXN];
    int root[MAXN],a[MAXN],b[MAXN];
    int n,m,cnt,p;
    
    int getid(int x)
    {//离散化 
    	return lower_bound(b+1,b+1+p,x)-b;
    }
    void build(int l,int r,int &cur)
    {//建立空树 
    	nodes[cur].sum=nodes[cur].l=nodes[cur].r=0;
    	cur=++cnt;
    	if(l==r)return;
    	int mid=(l+r)>>1;
    	build(l,mid,nodes[cur].l);
    	build(mid+1,r,nodes[cur].r);
    }
    void update(int l,int r,int &cur,int pre,int pos)
    {
    	cur=++cnt;//表示新开的节点 
    	nodes[cur].l=nodes[pre].l;
    	nodes[cur].r=nodes[pre].r;
    	nodes[cur].sum=nodes[pre].sum+1;//记录该点该时间之前有多少的数字 
    	if(l==r)return ;
    	int mid=(l+r)>>1;//每次按照pos的值插入到1~p的范围中 
    	if(pos<=mid)update(l,mid,nodes[cur].l,nodes[pre].l,pos);
    	else update(mid+1,r,nodes[cur].r,nodes[pre].r,pos);
    }
    int query(int l,int r,int x,int y,int key)
    {
    	if(l==r)return l;
    	int mid=(l+r)>>1;
    	int sum=nodes[nodes[y].l].sum-nodes[nodes[x].l].sum;//时间区间内个数 
    	if(key<=sum)//查找右区间 
    		return query(l,mid,nodes[x].l,nodes[y].l,key);
    	else//查找左区间 
    		return query(mid+1,r,nodes[x].r,nodes[y].r,key-sum);
    }
    void start()
    {
    	int x,y,k;
    
    	memset(root,0,sizeof(root));
    	cnt=0;
    	FOR2(i,1,n)
    	{
    		cin>>a[i];
    		b[i]=a[i];
    	}
    	cnt=0;
    	sort(b+1,b+n+1);//离散化 
    	p=unique(b+1,b+n+1)-b-1;//数据范围 
    	build(1,p,root[0]); //建立时间点为0 的空树 
    	FOR2(i,1,n)//i表示时间 ,按时间插入a[i]元素到线段树 
    		update(1,p,root[i],root[i-1],getid(a[i])); //按照a[i]在b[i]中的大小插入到不同位置 
    	while(m--)
    	{
    		cin>>x>>y>>k;//k=y-x-k+2;第K大和第K小的差别 
    		cout<<b[query(1,p,root[x-1],root[y],k)]<<endl;//区间第K大就是求 时间区间的第K大 
    	}
    }
    int main()
    {
    	int t;
    	cin>>t;
    	while(t--)
    	{
    	memset(root,0,sizeof(root));
    		cin>>n>>m;
    		start();
    	}
    }
    

    原hdu 4348 To the moon

    题意

    区间和线段树的可持久化,典型板子题,C表示区间加,Q表示区间查询,H表示区间查询第t时间的值,B表示返回第t时间,之后无法在前进。

    基本思路

    首先,需要学会基本的懒节点标记的区间和线段树,然后改,具体看代码,就是加了一层root时间节点
    数组一定要开到25W左右,20W以下会爆越界错!不能开太大(超过28W,像我用结构体没空间优化),就会爆内存T_T

    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <cmath>
    #include <cctype>
    #include <iostream>
    #include <algorithm>
    #include <string>
    #include <vector>
    #include <queue>
    #include <map>
    #include <set>
    #include <sstream>
    #include<iomanip>
    #define FOR(i,a,b) for(int i=a;i<b;i++)
    #define FOR2(i,a,b) for(int i=a;i<=b;i++)
    #define sync ios::sync_with_stdio(false);cin.tie(0) 
    #define ll long long
    #define MAXN 100010
    using namespace std;
    typedef struct {
    	int l,r;
    	ll w,laz;
    }NODE; NODE nodes[2500010];
    int n,m,root[MAXN],cnt,x,y,d,t,now,num;
    ll ans;
    
    int build(int l,int r)
    {
    	int cur;
    	cur=num++;
    	nodes[cur].l=nodes[cur].r=nodes[cur].w=nodes[cur].laz=0;
    	if(l==r)
    	{
    		cin>>nodes[cur].w;
    		return cur;
    	}
    	int mid=(l+r)>>1;
    	nodes[cur].l=build(l,mid);
    	nodes[cur].r=build(mid+1,r);
    	nodes[cur].w=nodes[nodes[cur].l].w+nodes[nodes[cur].r].w;//递归建树 
    	return cur;//返回节点坐标 
    }
    
    int update(int pre,int x,int y,int l,int r,int d)
    {
    	int cur=num++;
    	nodes[cur]=nodes[pre];
    	nodes[cur].w+=d*(min(y,r)-max(x,l)+1);
    	if(x<=l&&r<=y)
    	{
    		nodes[cur].laz+=d;//懒节点,建议使用返回值的函数 
    		return cur;
    	}
    	int mid=(l+r)/2;
    	if(x<=mid)
    	{//左区间查询 
    		nodes[cur].l=update(nodes[cur].l,x,y,l,mid,d);
    	}
    	if(y>mid)
    	{//右区间查询 
    		nodes[cur].r=update(nodes[cur].r,x,y,mid+1,r,d);
    	}
    	return cur;//返回节点坐标 
    }
    
    ll query(int cur,int x,int y,int l,int r)
    {
    	ll res=nodes[cur].laz*(min(y,r)-max(x,l)+1);//懒节点不用下传,这是返回函数的好处,不返回值的函数需要每次修改节点的w和子节点的laz标志 
    	if(x<=l&&r<=y)
    	{
    		return nodes[cur].w;
    	}
    	int mid=(l+r)/2;
    	if(x<=mid)res+=query(nodes[cur].l,x,y,l,mid);
    	if(y>mid)res+=query(nodes[cur].r,x,y,mid+1,r);
    	return res;
    }
    
    int main()
    {
    	bool flag=false;
    	while(cin>>n>>m)
    	{
    		if(flag)cout<<endl;
    		else flag=true;
    		memset(root,0,sizeof(root));
    		memset(nodes,0,sizeof(nodes));
    		num=now=0;
    		root[now]=build(1,n);
    		while(m--)
    		{
    			char op;cin>>op;
    			while(op=='
    ')cin>>op;
    			if(op=='C')
    			{
    				cin>>x>>y>>d;
    				now++;//线段树时间坐标 
    				root[now]=update(root[now-1],x,y,1,n,d);
    			}
    			if(op=='Q')
    			{
    				cin>>x>>y;
    				cout<<query(root[now],x,y,1,n)<<endl;
    			}
    			if(op=='H')
    			{ 
    				cin>>x>>y>>t;
    				cout<<query(root[t],x,y,1,n)<<endl;
    			}
    			if(op=='B')
    			{
    				cin>>now;
    			}
    		}
    	}
    	return 0;
    }
    

    原hdu 6278 Just h-index

    题意

    与hdu 2665 类似,查找区间内第(K)小的数(a_k),使得满足(a_k-1<=){大于(a_k-1)的个数}

    基本思路

    二分查找第K小,即mid,离散化查询第K-1个数,使得该数满足条件 。关键在于二分

    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <cmath>
    #include <cctype>
    #include <iostream>
    #include <algorithm>
    #include <string>
    #include <vector>
    #include <queue>
    #include <map>
    #include <set>
    #include <sstream>
    #include<iomanip>
    #define FOR(i,a,b) for(int i=a;i<b;i++)
    #define FOR2(i,a,b) for(int i=a;i<=b;i++)
    #define sync ios::sync_with_stdio(false);cin.tie(0) 
    #define ll long long
    #define MAXN 100010
    using namespace std;
    typedef struct{
    	int l,r;
    	ll w;
    }NODE;NODE nodes[24*MAXN];
    int root[24*MAXN],num,now,n,q,p;
    ll arr[MAXN],b[MAXN];
    int getid(ll x)
    {
    	return lower_bound(b,b+p,x)-b+1;
    }
    void build(int l,int r,int &cur)
    {//建立空树 
    	nodes[cur].l=nodes[cur].r=nodes[cur].w=0;
    	num++;
    	cur=num;
    	if(l==r)return  ;
    	int mid=(l+r)>>1;
    	build(l,mid,nodes[cur].l);
    	build(mid+1,r,nodes[cur].r);
    }
    void update(int l,int r,int &cur,int pre,int pos)
    {
    	num++;//建立新节点
    	cur=num;
    	nodes[cur].l=nodes[pre].l;
    	nodes[cur].r=nodes[pre].r;
    	nodes[cur].w=nodes[pre].w+1;
    	if(l==r)return;
    	int mid=(l+r)>>1;
    	if(pos<=mid)update(l,mid,nodes[cur].l,nodes[pre].l,pos);
    	else update(mid+1,r,nodes[cur].r,nodes[pre].r,pos);
    }
    
    int query(int l,int r,int x,int y,int key)
    {
    	if(l==r)return l;
    	int mid=(l+r)>>1;
    	int c=nodes[nodes[y].l].w-nodes[nodes[x].l].w;//区间个数 
    	if(key<=c)return query(l,mid,nodes[x].l,nodes[y].l,key);//左区间
    	else return query(mid+1,r,nodes[x].r,nodes[y].r,key-c);//右区间 
    }
    void start()
    {
    	p=num=now=0;
    	
    	FOR(i,0,n)
    	{
    		cin>>arr[i];
    		b[i]=arr[i];
    	}
    	
    	sort(b,b+n);
    	p=unique(b,b+n)-b;//离散化 
    	
    	build(1,p,root[0]);
    	FOR(i,0,n)update(1,p,root[i+1],root[i],getid(arr[i]));
    	
    	while(q--)
    	{
    		int x,y;cin>>x>>y;
    		int l=1,r=y-x+1,rr=r,ans=1;//l r表示查询的区间大小,确定mid 
    		while(l<=r)
    		{
    			int mid=(l+r)>>1;
    			int t=query(1,p,root[x-1],root[y],mid);//求区间第mid大的数 
    //			cout<<"mid="<<mid<<"t="<<t<<"b="<<b[t-1]<<endl;
    			if(b[t-1]>=rr-mid+1) {
    				ans=rr-mid+1;
    				r=mid-1; 
    			}
    			else l=mid+1;
    		}
    		cout<<ans<<endl;
    	}
    }
    int main()
    {
    	while(cin>>n>>q)
    	{
    		start();
    	}
    	return 0;
    }
    
  • 相关阅读:
    SAP系统邮件功能配置
    SAP SQL 表inner join 不同长度字段连接
    Read_text 获取传入参数
    imageio.write 惹的祸,占用cpu过高,堆溢出问题
    消息队列报 堆溢出解决方案
    技术文档java
    maven:项目中一些依赖不能更新可使用如下命令进行更新 maven库
    volatile 验证 java
    用Lock 和Newcondition实现同步容器 java
    计算数组中有几对相反数
  • 原文地址:https://www.cnblogs.com/tldr/p/11246857.html
Copyright © 2020-2023  润新知