zoukankan      html  css  js  c++  java
  • 树套树总结

    最近做题发现自己并不知道什么时候该用树套树,就来总结一下

    一、静态整体kth

    排序输出

    	sort(a+1,a+n+1);
    	printf("%d
    ",a[k]); 
    

    时间复杂度O(nlogn) 空间复杂度O(n)

    二、动态整体kth

    权值线段树+二分

    查询时先查询左子树和sum,比较k和sum的大小:若k<=sum则说明第k小数在左子树中,递归查询左子树;

    否则,这个数对应的就是右子树中第k-sum小的数,k-=sum,递归查询右子树。

    时间复杂度O(nlogn) 空间复杂度O(n)

    应该是这么写的吧……甚至离散化刚开始都写错了

    #include <iostream>
    #include <algorithm>
    #include <cstdio>
    #include <cstring>
    using namespace std;
    int read(){
    	int x = 1,a = 0;char ch = getchar();
    	while (ch < '0'||ch > '9'){if (ch == '-') x = -1;ch = getchar();}
    	while (ch >= '0'&&ch <= '9'){a = a*10+ch-'0';ch = getchar();}
    	return x*a;
    }
    const int maxn = 1e5+10;
    int sum[maxn << 1];
    int ls(int x){return x << 1;}
    int rs(int x){return x << 1 | 1;} 
    void modify(int x,int l,int r,int p,int k){
    	sum[x] += k;
    	if (l == r) return;
    	int mid = (l+r >> 1);
    	if (p <= mid) modify(ls(x),l,mid,p,k);
    	else modify(rs(x),mid+1,r,p,k);
    } 
    int query(int x,int l,int r,int k){
    //	cout<<l<<" "<<r<<" "<<sum[ls(x)]<<" "<<k<<endl;
    	int mid = (l+r >> 1);
    	if (l == r) return l;
    	if (sum[ls(x)] >= k) return query(ls(x),l,mid,k);
    	else return query(rs(x),mid+1,r,k-sum[ls(x)]);
    }
    int n,m;
    int a[maxn],b[maxn];
    int main(){
    	n = read(),m = read();
    	for (int i = 1;i <= n;i++) a[i] = b[i] = read();
    	sort(b+1,b+n+1);
    	int len = unique(b+1,b+n+1)-b-1;
    	for (int i = 1;i <= n;i++) a[i] = lower_bound(b+1,b+len+1,a[i])-b;
    	for (int i = 1;i <= n;i++) modify(1,1,len,a[i],1);
    	for (int i = 1;i <= m;i++){
    		int op = read(); 
    		if (op == 0){
    			int x = read(),k = read();
    			modify(1,1,len,a[x],-1);
    			a[x] = k;
    			modify(1,1,len,a[x],1);
    		}
    		if (op == 1){
    			int k = read();
    			printf("%d
    ",b[query(1,1,len,k)]);
    		}
    	}
    	return 0;
    }
    

    三、静态区间kth

    对每个点以其前缀开一棵权值线段树,那么任意一段区间均可以表示成为两棵权值线段树作差,即R位置的线段树减去L-1位置上的线段树

    每个点开一棵线段树空间复杂度(O(n^2)),MLE,考虑到后一个位置相比于前一个位置的更改只有logn个节点,所以使用主席树

    时间复杂度O(nlogn) 空间复杂度O(nlogn)

    #include <iostream>
    #include <algorithm>
    #include <cstdio>
    #include <cstring>
    using namespace std;
    int read(){
    	int x = 1,a = 0;char ch = getchar();
    	while (ch < '0'||ch > '9'){if (ch == '-') x = -1;ch = getchar();}
    	while (ch >= '0'&&ch <= '9'){a = a*10+ch-'0';ch = getchar();}
    	return x*a;
    }
    const int maxn = 1e5+10;
    int sum[maxn << 1];
    int tot,root[maxn];
    struct node{
    	int ls,rs,val;
    }tree[maxn*30];
    void modify(int &now,int lst,int l,int r,int p,int k){
    	if (!now) now = ++tot;
    	tree[now].val = tree[lst].val + k;
    	if (l == r) return;
    	int mid = (l+r >> 1);
    	if (p <= mid) tree[now].rs = tree[lst].rs,modify(tree[now].ls,tree[lst].ls,l,mid,p,k);
    	else tree[now].ls = tree[lst].ls,modify(tree[now].rs,tree[lst].rs,mid+1,r,p,k); 
    } 
    int query(int now,int lst,int l,int r,int k){
    	if (!now) return 0;
    	int mid = (l+r >> 1);
    	if (l == r) return l;
    	int res = tree[tree[now].ls].val-tree[tree[lst].ls].val;	
    	if (res >= k) return query(tree[now].ls,tree[lst].ls,l,mid,k);
    	else return query(tree[now].rs,tree[lst].rs,mid+1,r,k-res);
    }
    int n,m;
    int a[maxn],b[maxn];
    int main(){
    	n = read(),m = read();
    	for (int i = 1;i <= n;i++) a[i] = b[i] = read();
    	sort(b+1,b+n+1);
    	int len = unique(b+1,b+n+1)-b-1;
    	for (int i = 1;i <= n;i++) a[i] = lower_bound(b+1,b+len+1,a[i])-b;
    	for (int i = 1;i <= n;i++) modify(root[i],root[i-1],1,len,a[i],1);
    	for (int i = 1;i <= m;i++){
    		int l = read(),r = read(),k = read();
    		printf("%d
    ",b[query(root[r],root[l-1],1,len,k)]);
    	}
    	return 0;
    }
    

    四、动态区间kth

    还是要想办法维护前缀和。如果只是同3的前缀和的话,就要对前缀和进行O(nlogn)的单次修改,显然TLE。

    这里考虑用树状数组维护前缀和。修改时,可以只修改logn个位置,复杂度(O(log^2n))

    查询时,依旧是R位置减去L-1位置,这时候不再是两棵线段树作差,而是log棵线段树与log棵线段树作差;跳的时候,log个节点一起跳到左子树/右子树

    时间复杂度(O(nlog^2n)) 空间复杂度O(nlogn)

    #include <iostream>
    #include <algorithm>
    #include <cstdio>
    #include <cstring>
    using namespace std;
    int read(){
    	int x = 1,a = 0;char ch = getchar();
    	while (ch < '0'||ch > '9'){if (ch == '-') x = -1;ch = getchar();}
    	while (ch >= '0'&&ch <= '9'){a = a*10+ch-'0';ch = getchar();}
    	return x*a;
    }
    const int maxn = 1e5+10;
    int n,m;
    int a[maxn],b[maxn];
    int ql[maxn],qr[maxn],qk[maxn];
    struct node{
    	int ls,rs,sum;
    }tree[maxn*30];
    int tot,a1[maxn],a2[maxn],cnt1,cnt2;
    void add(int &x,int l,int r,int p,int k){
    	if (!x) x = ++tot;
    	tree[x].sum += k;
    	int mid = (l+r >> 1);
    	if (l == r) return;
    	if (p <= mid) add(tree[x].ls,l,mid,p,k);
    	else add(tree[x].rs,mid+1,r,p,k);
    }
    int root[maxn],len;
    void build(int x,int k){for (int i = x;i <= n;i += i&-i) add(root[i],1,len,a[x],k);}
    int getkth(int l,int r,int k){
    	int res = 0,mid = (l+r >> 1);
    	if (l == r) return l;
    	for (int i = 1;i <= cnt1;i++) res += tree[tree[a1[i]].ls].sum;
    	for (int i = 1;i <= cnt2;i++) res -= tree[tree[a2[i]].ls].sum;
    	if (res >= k){
    		for (int i = 1;i <= cnt1;i++) a1[i] = tree[a1[i]].ls;
    		for (int i = 1;i <= cnt2;i++) a2[i] = tree[a2[i]].ls;
    		return getkth(l,mid,k);
    	}
    	else{
    		for (int i = 1;i <= cnt1;i++) a1[i] = tree[a1[i]].rs;
    		for (int i = 1;i <= cnt2;i++) a2[i] = tree[a2[i]].rs;
    		return getkth(mid+1,r,k-res);
    	}
    }
    int query(int l,int r,int k){
    	cnt1 = 0,cnt2 = 0;
    	for (int i = r;i;i -= i&-i) a1[++cnt1] = root[i];
    	for (int i = l-1;i;i -= i&-i) a2[++cnt2] = root[i];
    	return getkth(1,len,k);
    }
    int cnt;
    int main(){
    	n = read(),m = read();
    	for (int i = 1;i <= n;i++) a[i] = b[++cnt] = read();
    	for (int i = 1;i <= m;i++){
    		char op[10];scanf ("%s",op);
    		if (op[0] == 'Q') ql[i] = read(),qr[i] = read(),qk[i] = read();
    		if (op[0] == 'C') ql[i] = read(),qk[i] = b[++cnt] = read();
    	}
    	sort(b+1,b+cnt+1);
    	len = unique(b+1,b+cnt+1)-b-1;
    	for (int i = 1;i <= n;i++) a[i] = lower_bound(b+1,b+len+1,a[i])-b;
    	for (int i = 1;i <= n;i++) if (!qr[i]) qk[i] = lower_bound(b+1,b+len+1,qk[i])-b; 
    	for (int i = 1;i <= n;i++) build(i,1);
    	for (int i = 1;i <= m;i++){
    		if (qr[i]) printf("%d
    ",b[query(ql[i],qr[i],qk[i])]);
    		else build(ql[i],-1),a[ql[i]] = qk[i],build(ql[i],1);
    	} 
    	return 0;
    }
    
  • 相关阅读:
    Spring Cloud Hystrix Dashboard的使用 5.1.3
    Spring Cloud Hystrix 服务容错保护 5.1
    Spring Cloud Ribbon 客户端负载均衡 4.3
    Spring Cloud 如何实现服务间的调用 4.2.3
    hadoop3.1集成yarn ha
    hadoop3.1 hdfs的api使用
    hadoop3.1 ha高可用部署
    hadoop3.1 分布式集群部署
    hadoop3.1伪分布式部署
    KVM(八)使用 libvirt 迁移 QEMU/KVM 虚机和 Nova 虚机
  • 原文地址:https://www.cnblogs.com/little-uu/p/14878467.html
Copyright © 2011-2022 走看看