标签:int 查询 += 树状 tchar amp get nod 前缀和
最近做题发现自己并不知道什么时候该用树套树,就来总结一下
排序输出
sort(a+1,a+n+1);
printf("%d\n",a[k]);
时间复杂度O(nlogn) 空间复杂度O(n)
权值线段树+二分
查询时先查询左子树和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\n",b[query(1,1,len,k)]);
}
}
return 0;
}
对每个点以其前缀开一棵权值线段树,那么任意一段区间均可以表示成为两棵权值线段树作差,即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\n",b[query(root[r],root[l-1],1,len,k)]);
}
return 0;
}
还是要想办法维护前缀和。如果只是同3的前缀和的话,就要对前缀和进行O(nlogn)的单次修改,显然TLE。
这里考虑用树状数组维护前缀和。修改时,可以只修改logn个位置,复杂度\(O(log^2n)\)
查询时,依旧是R位置减去L-1位置,这时候不再是两棵线段树作差,而是log棵线段树与log棵线段树作差;跳的时候,log个节点一起跳到左子树/右子树
时间复杂度\(O(nlog^2n)\) 空间复杂度O(nlogn)
标签:int 查询 += 树状 tchar amp get nod 前缀和
原文地址:https://www.cnblogs.com/little-uu/p/14878467.html