链接:
题意:
一开始,给定一个空序列,要求设计一种数据结构,支持下列两种操作:
①序列中加入x个y
②查询序列中最小的第x个数到第y个数的和
思路:
首先可以纯暴力的vector优化为multiset(自动排序)或者map(加入x个数只需要操作一次,查询某个数有多少个亦然),可是面对极端数据(如每个数均只有一个),又会退化为multiset,在查询复杂度O(n)的情况下,AC是不可能的。
想要办到区间查询,很自然想到线段树或者树状数组,但是他们不能支持排序,而且给的这些{y}不知道放在树状数组的哪个位置。假设先给一个5,放在了树状数组的[1],那么再给几个2,将无处可放。因此采取离线处理,将所有给定的数都读下来,排个序,离散化,从小到大分配一个id,就可以根据id分配位置了。
树状数组支持求和,用一个树状数组a记录数字*数量,一个树状数组b记录每个数的数量,采用二分法找到查询的区间[l,r]中l对应着哪个id(设为x),r对应着哪个id(设为y),则最终答案为:
其中,cnt[i]表示id为i的数有多少个,num[i]代表id为i的数离散化之前是多少。
值得注意的是,用树状数组维护数量的时候要注意不要取模,否则在二分时会出错。写代码时特地给函数加了参,表示是否取模。
代码:
TLE代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
static const int maxn = 100010;
static const int INF = 0x3f3f3f3f;
static const int mod = (int)1e9 + 7;
static const double eps = 1e-6;
static const double pi = acos(-1);
void redirect(){
#ifdef LOCAL
freopen("test.txt","r",stdin);
#endif
}
map<ll,ll>m;
int main(){
redirect();
int q,x;
ll y,z;
scanf("%d",&q);
while(q--){
scanf("%d %lld %lld",&x,&y,&z);
if(x==1){
m[z] += y;
}
else{
ll l = y,r = z;
ll already = 0,sum = 0;
for(auto it = m.begin();it != m.end();it++){
if(already < l){
if(it->second + already >= l){
if(it->second + already <= r){
sum += (already+it->second-l+1)*it->first;
sum %= mod;
}
else{
sum += (r-l+1)*it->first;
sum %= mod;
}
}
}
else if(already <= r){
if(already + it->second <= r){
sum += it->second*it->first;
sum %= mod;
}
else{
sum += (r-already)*it->first;
}
}
else{
break;
}
already += it->second;
}
printf("%lld\n",sum);
}
}
return 0;
}
AC代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
static const int maxn = 100010;
static const int INF = 0x3f3f3f3f;
static const int mod = (int)1e9 + 7;
static const double eps = 1e-6;
static const double pi = acos(-1);
void redirect(){
#ifdef LOCAL
freopen("test.txt","r",stdin);
#endif
}
struct node{
int type;
ll x,y;
}p[maxn];
struct tree{
ll c[maxn<<2];
inline int lowbit(int k){
return k & (-k);
}
inline void add(int x,ll k,bool mo=false){
int n = maxn<<2-1;
while(x <= n){
c[x] += k;
if(mo)c[x] %= mod;
x += lowbit(x);
}
}
inline ll prefix(int x,bool mo=false){
ll ans = 0;
while(x != 0){
ans += c[x];
if(mo)ans %= mod;
x -= lowbit(x);
}
return ans;
}
inline ll sum(int l,int r,bool mo=false){
if(mo)return (mod + prefix(r,true) - prefix(l-1,true)) % mod;
return prefix(r) - prefix(l-1);
}
}a,b;
int w[maxn];
ll point[maxn];
set<int>s;
map<int,int>mp;
int main(){
redirect();
int q;
scanf("%d",&q);
for(int i = 1;i <= q;i++){
scanf("%d %lld %lld",&p[i].type,&p[i].x,&p[i].y);
if(p[i].type == 1)s.insert(p[i].y);
}
int cnt = 0;
for(auto& x : s)w[++cnt] = x,mp[x] = cnt;
s.clear();
for(int i = 1;i <= q;i++){
ll& x = p[i].x,&y = p[i].y;
if(p[i].type==1){
a.add(mp[y],x*y%mod,true);
b.add(mp[y],x);
point[mp[y]] += x;
}
else{
int l = 1,r = cnt,mid;
while(l < r){
mid = (l+r)>>1;
if(b.prefix(mid) >= x)r = mid;
else l = mid+1;
}
int from = l;
r = cnt;
while(l < r){
mid = (l+r)>>1;
if(b.prefix(mid) >= y)r = mid;
else l = mid+1;
}
int to = l;
printf("%lld\n",(2*mod + a.sum(from,to,true) - (point[from]-(b.prefix(from)-x+1))*w[from]%mod - (b.prefix(to)-y)*w[to]%mod)%mod);
}
}
return 0;
}