hdu6464 广工大第十四届程序设计竞赛 D.免费送气球 离线处理+离散化+树状数组+二分

链接:

        免费送气球

题意:

        一开始,给定一个空序列,要求设计一种数据结构,支持下列两种操作:

        ①序列中加入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),则最终答案为:

        \small \sum_{i=x}^{y}cnt[i]\cdot num[i]-(cnt[x]-(\sum_{i=1}^{x}cnt[i]-l+1))\cdot num[x]-(\sum_{i=1}^{y}cnt[i]-r)\cdot num[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;
}

猜你喜欢

转载自blog.csdn.net/krypton12138/article/details/88609911
今日推荐