HDU 6464 /// 权值线段树
Posted zquzjx
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HDU 6464 /// 权值线段树相关的知识,希望对你有一定的参考价值。
题目大意:
共Q次操作 操作有两种
操作一 在序列尾部加入f[i]个s[i]
操作二 查询序列第f[i]小到第s[i]小之间的总和
离线操作 把序列内的值离散化
然后利用离散化后的值 在线段树上对应权值操作
权值线段树维护权值对应的值的个数和总和
查询 用s[i]的前缀和减去f[i]-1的前缀和 具体看注释
#include <bits/stdc++.h> using namespace std; #define LL long long #define INF 0x3f3f3f3f #define mem(i,j) memset(i,j,sizeof(i)) #define inc(i,l,r) for(int i=l;i<=r;i++) #define dec(i,r,l) for(int i=r;i>=l;i--) #define gcd(i,j) __gcd(i,j); const int N=1e5+5; const int mod=1000000007; LL p[N], f[N], s[N]; LL cop[N], tot; #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 LL num[N<<2], sum[N<<2]; void pushUp(int rt) { num[rt]=num[rt<<1]+num[rt<<1|1]; sum[rt]=(sum[rt<<1]+sum[rt<<1|1])%mod; } void update(LL ind,LL k,int l,int r,int rt) { if(l==r) { num[rt]+=k; // 这个值在序列内的个数 sum[rt]=(sum[rt]+cop[ind]*k)%mod; // 总和 return ; } int m=(l+r)>>1; if(m>=ind) update(ind,k,lson); else update(ind,k,rson); pushUp(rt); } LL query(LL k,int l,int r,int rt) { if(k==0) return 0; if(l==r) return k*cop[l]%mod; // 在l位置 还差k个 k可能不需要num[l]那么多 // 所以应该是k*cop[l] 而不是sum[l] int m=(l+r)>>1, L=rt<<1; if(num[L]<=k) return (sum[L]+query(k-num[L],rson))%mod; // 左儿子区间不足k个 那么左儿子区间的总和+右儿子区间差的个数的总和 else return query(k,lson); // 左儿子区间的数已超过k个 就在左儿子区间内继续缩小 } int main() { mem(num,0LL); mem(sum,0LL); int q; scanf("%d",&q); inc(i,1,q) scanf("%lld%lld%lld",&p[i],&f[i],&s[i]); tot=0; inc(i,1,q) if(p[i]==1) cop[++tot]=s[i]; sort(cop+1,cop+1+tot); tot=unique(cop+1,cop+1+tot)-cop-1; inc(i,1,q) { if(p[i]==1) { int ind=lower_bound(cop+1,cop+1+tot,s[i])-cop; update(ind,f[i],1,tot,1); // 离散化后的值在对应权值位置操作 } else { LL R=query(s[i],1,tot,1); LL L=query(f[i]-1,1,tot,1); printf("%lld ",(R-L+mod)%mod); } } return 0; }
以上是关于HDU 6464 /// 权值线段树的主要内容,如果未能解决你的问题,请参考以下文章