可持久化线段树(poj2104/hdu4866)

最近网络太卡不得不从Logdown转回来

[这里有个博客说的很详细]( http://blog.csdn.net/metalseed/article/details/8045038) 
我来补充一下自己的想法 
大概就是 
每次更新一条链都新开一路的节点 
和历史版本共用相同节点 
用root[]数组保存每颗线段树根 
注意要写得很清楚,不然很容易超时超内存什么的

问题模型 

1.静态区间第K大 POJ 2104 

给出N个数,每次询问第x个数到第y个数区间中第k大 

分析 

离散化,对每个前缀区间 a[1]...a[x]都建一颗线段树,那么我们询问区间[x,y]就用第y棵线段树的对应区间-第x-1棵树的对应区间,然后看这个区间的数够不够K个,多余就往左走,不足往右走,直到l==r,就可以返回第l个数就是我们要找的数 
每次我们先默认当前树结点和历史版本相同,即共用子节点,然后判断往左走还是往右走,就可以实现只新建被更新的子节点,而共用不被更新的子节点 

代码 

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
#include<string>
using namespace std;
#define rep(frep,ss,tt) for (frep=ss;frep<=tt;++frep)
#define drep(frep,tt,ss) for (frep=tt;frep>=ss;--frep)
#define inf 0x3f3f3f3f
#define N 100001
#define num(x) T[x].num
#define ls(x) T[x].ls
#define rs(x) T[x].rs
struct Tree{
	int num,ls,rs;
}T[N*20];
int sz,n,m;
int a[N],p[N],hash[N],root[N];
inline void insert(int brt,int &rt,int l,int r,int x){ //注意这里的 &符号,可以修改外部变量
	rt=++sz;
	T[rt]=T[brt];
	++num(rt);
	if (l==r) return;
	int mid=(l+r)>>1;
	if (x<=mid) insert(ls(brt),ls(rt),l,mid,x);
	else insert(rs(brt),rs(rt),mid+1,r,x);
}
int query(int brt,int rt,int l,int r,int k){
	if (l==r) return l;
	int t=num(ls(rt))-num(ls(brt));
	int mid=(l+r)>>1;
	if (t>=k) return query(ls(brt),ls(rt),l,mid,k);
	else  return query(rs(brt),rs(rt),mid+1,r,k-t);
}
bool cmp(int i,int j){ return a[i]<a[j];}

int main(){
  scanf("%d%d",&n,&m);
  int i,x,y,k;
  rep(i,1,n) scanf("%d",&a[i]),p[i]=i;
	sort(p+1,p+1+n,cmp);
	rep(i,1,n) hash[p[i]]=i; //离散化
	rep(i,1,n)
		insert(root[i-1],root[i],1,n,hash[i]);
	rep(i,1,m){
		scanf("%d%d%d",&x,&y,&k);
		int f=query(root[x-1],root[y],1,n,k);
		printf("%d\n",a[p[f]]);
	}
  return 0;
}

2.hdu4866 每次找离x轴最近的k条线,计算得分

分析


坐标范围不大,可以不用离散化,省点时间

这题改了2天又是TLE又是MLE的= =

线段按d从小到大依次插入,建立n棵线段树

维护的是区间,所以用打tag的方式,涉及到标记下传的问题,不过这题只有加法操作,一路加下去就好了可以不用下传

每次查询一个位置,我们用二分查找对应的线段树,这颗树的这个位置上有<=k条线段(可能有不足k条),然后在这颗树上统计得分即可

这题数组需要开蛮大的,用结构体好像会浪费内存?!= =分成几个数组就过了ORZ


代码


#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define rep(frep,ss,tt) for (frep=ss;frep<=tt;++frep)
#define drep(frep,tt,ss) for (frep=tt;frep>=ss;--frep)
#define inf 0x3f3f3f3f
#define NN 100005
#define MAXN 5900100
struct Seg{
	int l,r,d;
}seg[NN];
int ls[MAXN],rs[MAXN],num[MAXN];
long long sum[MAXN];
int tot,sz,N,X,M,root[NN];
long long P;

bool cmp(const Seg &a,const Seg &b){ 
	return a.d<b.d || (a.d==b.d && a.l<b.l) || (a.d==b.d && a.l==b.l && a.r<b.r);
}

inline void insert(int brt,int &rt,int l,int r,int s,int t,int d){
	rt=++sz;
	sum[rt]=sum[brt]; num[rt]=num[brt]; ls[rt]=ls[brt]; rs[rt]=rs[brt];
	if (s<=l && r<=t){
		sum[rt]+=d;
		num[rt]++;
		return;
	}
	int mid=(l+r)>>1;
	if (s<=mid) insert(ls[brt],ls[rt],l,mid,s,t,d);
	if (t>mid)  insert(rs[brt],rs[rt],mid+1,r,s,t,d);
}

int Num_query(int rt,int l,int r,int x){
	int ans=num[rt];
	if (l!=r){
	   int mid=(l+r)>>1;
	   if (x<=mid) ans+=Num_query(ls[rt],l,mid,x);
	      else ans+=Num_query(rs[rt],mid+1,r,x);
	}
	return ans;
}

long long Sum_query(int rt,int l,int r,int x){
	long long ans=sum[rt];
	if (l!=r){
	    int mid=(l+r)>>1;
	    if (x<=mid) ans+=Sum_query(ls[rt],l,mid,x);
	       else ans+=Sum_query(rs[rt],mid+1,r,x);
	}
	return ans;
}

int search(int pos,int num){
	int l=1,r=N,mid;
	while (l<=r){
		mid=(l+r)>>1;
		int calc=Num_query(root[mid],1,X,pos);
		if (calc==num) return mid;
		if (calc<num) l=mid+1;
		else r=mid-1;
	}
	return l;
}

int main(){
	//freopen("xx.in","r",stdin);
    int i,x; long long a,b,c;
	while (~scanf("%d%d%d%I64d",&N,&M,&X,&P)){
    sz=0;
  	rep(i,1,N) scanf("%d%d%d",&seg[i].l,&seg[i].r,&seg[i].d);
	sort(seg+1,seg+1+N,cmp);
	rep(i,1,N) insert(root[i-1],root[i],1,X,seg[i].l,seg[i].r,seg[i].d);
    long long pre=1,k;
	rep(i,1,M){
		scanf("%d%I64d%I64d%I64d",&x,&a,&b,&c);
		k=(a*pre+b)%c;
		if (!k) { puts("0"); pre=0; continue; }
		int pos=search(x,k);
		long long ans=Sum_query(root[pos],1,X,x);
		if (pre>P) ans<<=1;
		printf("%I64d\n",ans);
		pre=ans;
	}
    }
}





你可能感兴趣的:(可持久化线段树(poj2104/hdu4866))