权值线段树
权值线段树的本质是线段树维护桶。
权值线段树维护一个区间内出现数的个数。
和普通线段树的区别
权值线段树维护的是桶,按值域开空间,维护的是个数。
简单线段树维护的是信息,按个数可开空间,维护的是特定信息。
用途
-
求一段区间内数出现的次数
-
比 x 小的数的个数
-
比 x 大的数的个数
-
区间第 k 大的数
-
区间第 k 小的数。
一般和动态开点一块使用,可以大大减少时间和空间。
例题
见code:
const int N=1e6;
const int INF=1e7;
int n, op, x, tot, root;
struct node{
int dat,l,r;
}t[N<<2];
il int read(){
int f = 1, x = 0; char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-')f = -f;ch = getchar();}
while(ch >= '0' && ch <= '9'){x = x*10 + ch-'0';ch = getchar();}
return f * x;
}
void pushup(int k){t[k].dat=t[t[k].l].dat+t[t[k].r].dat;}
void Insert(int &pos,int l,int r,int x,int val) {//加&相当于引用。
if(!pos) {pos=++tot;}//一边找一边更新左右儿子。
if(l==r) {
t[pos].dat+=val;
return ;
}
int mid=(l+r)>>1;
if(x<=mid)
Insert(t[pos].l,l,mid,x,val);
else
Insert(t[pos].r,mid+1,r,x,val);
pushup(pos);
}
int query_kth(int k,int l,int r,int fl,int fr){
if(!k) return 0;
if(l>=fl&&r<=fr) return t[k].dat;
int res=0,mid=(l+r)>>1;
if(fl<=mid)
res+=query_kth(t[k].l,l,mid,fl,fr);
if(fr>mid)
res+=query_kth(t[k].r,mid+1,r,fl,fr);
return res;
}
int kth(int k,int l,int r,int x){
if(!k) return -1;
if(l==r) return l;
int mid=(l+r)>>1,left=t[t[k].l].dat;
if(x<=left)
return kth(t[k].l,l,mid,x);
return kth(t[k].r,mid+1,r,x-left);
}
signed main(){
n = read();
while(n--) {
op = read(); x = read();
if(op==1) {//插入
Insert(root,-INF,INF,x,1);
}
else if(op==2) {//删除
Insert(root,-INF,INF,x,-1);
}
else if(op==3) {//查询k的排名
cout<<query_kth(root,-INF,INF,-INF,x-1)+1<<'\n';//查询-1e7~x-1有多少数,然后+1
}
else if(op==4) {//查询排名为k的数
cout<<kth(root,-INF,INF,x)<<'\n';
}
else if(op==5) {//求x的前驱
int p=query_kth(root,-INF,INF,-INF,x-1);
cout<<kth(root,-INF,INF,p)<<'\n';
}
else {//求x的后继
int p=query_kth(root,-INF,INF,-INF,x)+1;
cout<<kth(root,-INF,INF,p)<<'\n';
}
}
return 0;
}
/*
求有多少区间和 < t
转化为
Q[r]-Q[l-1]<t
Q[l-1]>Q[r]-t
用权值线段树维护前缀和
枚举Q[r]
*/
const int N=1e6+10;
const int M=1e7+10;
int n, len, x, ans, root, tot, Q[N];
struct node{
int dat,l,r;
}t[M];
il int read(){
int f = 1, x = 0; char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-')f = -f;ch = getchar();}
while(ch >= '0' && ch <= '9'){x = x*10 + ch-'0';ch = getchar();}
return f * x;
}
void pushup(int k){t[k].dat=t[t[k].l].dat+t[t[k].r].dat;}
void Insert(int &k,int l,int r,int x){
if(!k) {k=++tot;}
if(l==r){
t[k].dat+=1;
return ;
}
int mid=(l+r)>>1;
if(x<=mid)
Insert(t[k].l,l,mid,x);
else Insert(t[k].r,mid+1,r,x);
pushup(k);
}
int Query(int k,int l,int r,int fl,int fr) {//查询比Q[i]大的个数
if(!k) return 0;
if(l>=fl&&r<=fr) return t[k].dat;
int res=0,mid=(l+r)>>1;
if(fl<=mid)
res+=Query(t[k].l,l,mid,fl,fr);
if(fr>mid)
res+=Query(t[k].r,mid+1,r,fl,fr);
return res;
}
signed main(){
n = read();len = read();
rep(i, 1, n) {
x = read();
Q[i]=Q[i-1]+x;
Insert(root,-inf,inf,Q[i-1]);
ans+=Query(root,-inf,inf,Q[i]-len+1,inf);
}
cout<<ans;
return 0;
}
这题卡longlong。而且卡数组大小。
/*
动态开点+线段树+区间修改+区间求和
*/
const int N=15000010;
int n, q, op, l, r, tot=1, root=1;
struct node{
int dat,l,r,lazy;
}t[N];
il int read(){
int f = 1, x = 0; char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-')f = -f;ch = getchar();}
while(ch >= '0' && ch <= '9'){x = x*10 + ch-'0';ch = getchar();}
return f * x;
}
void pushup(int k){t[k].dat=t[t[k].l].dat+t[t[k].r].dat;}
void pushdown(int k,int l,int r){
if(t[k].lazy==-1) return ;
int mid=(l+r)>>1;
if(!t[k].l){t[k].l=++tot;t[t[k].l].lazy=-1;}
if(!t[k].r){t[k].r=++tot;t[t[k].r].lazy=-1;}
t[t[k].l].dat=(mid-l+1)*t[k].lazy;
t[t[k].l].lazy=t[k].lazy;
t[t[k].r].dat=(r-(mid+1)+1)*t[k].lazy;
t[t[k].r].lazy=t[k].lazy;
t[k].lazy=-1;
return ;
}
void Insert(int &k, int l, int r,int fl,int fr,int val){//0是工作日 1是非工作日
if(!k) {k=++tot;t[k].lazy=-1;}
if(l>=fl&&r<=fr) {
t[k].dat=(r-l+1)*val;
t[k].lazy=val;
return ;
}
pushdown(k,l,r);
int mid=(l+r)>>1;
if(fl<=mid)
Insert(t[k].l,l,mid,fl,fr,val);
if(fr>mid)
Insert(t[k].r,mid+1,r,fl,fr,val);
pushup(k);
}
signed main() {
n = read();
q = read();
rep(i, 1, q){
l = read();r = read();op = read();
Insert(root,1,n,l,r,(op==1)?1:0);
cout<<(n-t[1].dat)<<'\n';
}
return 0;
}
这个题有个特ex的地方就是字符串输入卡scanf和 getchar。
一开始输入的时候用了getchar(),交上去一个点也没对,下载了一个数据结果发现输出没有错误。然后翻讨论区发现“用cin试试”。
得到一个\(trick\):字符串类的输入尽量用 cin,正确性高。
const int N=3e5+10;
const int B=1e5;
int n,M,x,tt,tot,ans,root,least,chg;
char op;
struct node{
int dat,l,r;
}t[N];
il int read(){
int f = 1, x = 0; char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-')f = -f;ch = getchar();}
while(ch >= '0' && ch <= '9'){x = x*10 + ch-'0';ch = getchar();}
return f * x;
}
void pushup(int k){t[k].dat=t[t[k].l].dat+t[t[k].r].dat;}
void Insert(int &k,int l,int r,int x){
if(!k) k=++tot;
if(l==r){
t[k].dat++;
return ;
}
int mid=(l+r)>>1;
if(x<=mid)
Insert(t[k].l,l,mid,x);
else Insert(t[k].r,mid+1,r,x);
pushup(k);
}
int Query(int k,int l,int r,int fl,int fr){
if(l>=fl&&r<=fr){
return t[k].dat;
}
int mid=(l+r)>>1,ans=0;
if(fl<=mid)
ans+=Query(t[k].l,l,mid,fl,fr);
if(fr>mid)
ans+=Query(t[k].r,mid+1,r,fl,fr);
return ans;
}
void Change(int k,int l,int r,int fl,int fr){
if(l==r){
t[k].dat=0;
return ;
}
int mid=(l+r)>>1;
if(t[t[k].l].dat&&fl<=mid) Change(t[k].l,l,mid,fl,fr);
if(t[t[k].r].dat&&fr>mid) Change(t[k].r,mid+1,r,fl,fr);
pushup(k);
}
int Kth(int k,int l,int r,int x){
if(!k) return -1;
if(l==r) return l;
int mid=(l+r)>>1;
if(t[t[k].r].dat>=x) return Kth(t[k].r,mid+1,r,x);
return Kth(t[k].l,l,mid,x-t[t[k].r].dat);
}
signed main(){
n=read();M=read();
least=M+B;
rep(i,1,n){
cin>>op;x=read();
if(op=='I') {
if(x<M) continue;
Insert(root,0,inf,x-chg+B);
}
else if(op=='A'){
least-=x;
chg+=x;
}
else if(op=='S'){
least+=x;
chg-=x;
if(least>=1&&Query(root,0,inf,0,least-1)>0){
ans+=Query(root,0,inf,0,least-1);
Change(root,0,inf,0,least-1);
}
}
else if(op=='F'){
if(x>Query(root,0,inf,least,inf)) cout<<"-1"<<'\n';
else cout<<Kth(root,0,inf,x)-B+chg<<'\n';
}
}
cout<<ans<<'\n';
return 0;
}

浙公网安备 33010602011771号