【树状数组|线段树】hdu_6464免费送气球

                                           免费送气球

Problem Description

又到了GDUT一年一度的程序设计竞赛校赛的时间啦。同学们只要参加校赛,并且每解出一道题目就可以免费获得由ACM协会和集训队送出的气球一个。听到这个消息,JMC也想参加免费拿气球。可是,由于JMC太菜了而被禁止参赛,于是他找到你想让你帮忙参加比赛,可以通过执行下面的C++程序解决问题后获得气球并送给他。JMC保证了下面的程序一定能获得正确的结果。

void solve(int Q, int type[], long long first[], long long second[]) {
    vector<long long> vec;
    for (int i = 0; i < Q; ++i) {
        if (type[i] == 1) {
            long long k = first[i], val = second[i];
            while (k--) {
                vec.push_back(val);
            }
        }
        else if (type[i] == 2) {
            sort(vec.begin(), vec.end());
            long long l = first[i] - 1, r = second[i], res = 0;
            while (l < r) {
                res = (res + vec[l++]) % 1000000007;
            }
            printf("%lld\n", res);
        }
    }
}

为防止你被JMC的代码搞到头晕目眩,JMC特意给出了问题的文字描述。已知一开始有一个空序列,接下来有Q次操作,每次操作给出type、first和second三个值。当type为1时,意味着该操作属于第一种操作:往序列尾部添加first个second数。当type为2时,意味着该操作属于第二种操作:查询序列中第first小至第second小的数值之和(一共有(second - first + 1)个数被累加),并将结果对1000000007取模后输出。

 Input

单组数据
第一行一个Q(1 <= Q <= 1e5),代表Q次操作。
接下来有Q行,每行包含三个整数type、first和second;其中1 <= type <= 2。当type等于1时,0 <= first,second < 1e9。当type等于2时,1 <= first <= second,且first和second均不大于目前已添加进序列的数的数量。

Output

对于每次操作二,将结果对1000000007取模后输出。

Sample Input

6

1 5 1

1 6 3

2 2 5

2 4 8

1 2 2

2 4 8 

Sample Output

4

11

9


 

树状数组方法主要参考龙哥的博客:

题目总结--“字节跳动-文远知行杯”广东工业大学第十四届程序设计竞赛

 

【题意】:

提供两种操作:

主要是两个操作:

第一种 type = 1时候,往后面添加 First 个 Second

第二种 type = 2时候,排序后,询问下标First 到 Second 的总和.

【题解】:

这个题目主要可以由两种方法来做,但是根本是一样的,一种是线段树,另外一种就是树状数组,

首先我们分析题目,如果在询问的时候排序肯定不行,但我们发现这个题目却能离线操作

然后看数据,数据大小是1e9,数据个数也是1e9,所以此时需要的是 离散化

维护两个数组的值,

一个数组维护的是: 个数

一个数组维护的是:区间和

我们在更新的时候 注意,我们直接离线排序后的下标进行更新。

在询问的时候其实就是:分三部分来求,

第一部分就是 :第一个下标所在的地方 (求个数×对应的值)

第二部分就是:第二个下标的所在的地方(求个数×对应的值)

第三部分就是:两个下标中间连续的一大块(直接用区间和)

 

贴上代码:(感谢龙哥)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6+100;
const int mod = 1e9+7;
typedef struct Node {
    ll x,y;
}Node ;
Node a[N];              //输入组 first,Second
ll b[N],tot;            // 数组b是 type = 1时的 数的种类,以及大小tot
ll c1[N],c2[N];         //c1数组是 个数,c2数组是 和
int op[N];              //下标对应的type

void add1(int No,ll val){   //更新个数
    while(No<N){
        c1[No] += val;
        No += (No&-No);
    }
}

void add2(int No,ll val){   //更新 个数对应的区间总和
    while(No<N){
        c2[No] += val;
        No += (No&-No);
    }
}

ll sum1(int No){            //求和 个数求和
    ll res = 0;
    while( No>0 ){
        res += (c1[No]);
        No -= No&-No;
    }
    return res;
}
ll sum2(int No){            //求和 区间的求和
    ll res = 0;
    while( No>0 ){
        res = (c2[No]+res)%mod;
        No -= (No & -No);
    }
    return res;
}
int getblock(ll x){         //知道某一次询问的下标在哪一块
    ll L = 1 ,R = tot,ans=1,Mid;
    while( L<=R ){
        Mid = (L+R) >> 1;
        if( sum1(Mid) < x ){
            L = Mid +1;
            ans = Mid;
        }else{
            R = Mid -1;
        }
    }
    return L ;
}
int main()
{
    int n,m;
    scanf("%d",&n);

    for(int i=1;i<=n;i++){
        scanf("%d%lld%lld",&op[i],&a[i].x,&a[i].y);
        if( op[i] == 1 ){   //离线处理
            b[++tot] = a[i].y;
        }
    }
    sort( b+1, b+1+tot);    //排序后,确保一直有序
    for(int i=1;i<=n;i++){

        if( op[i] == 1 ){
            int pos = lower_bound(b+1,b+1+tot,a[i].y)-(b);//更新的时候确保是合法的
            //printf("Pos %d\n",pos);
            ll tmp = (a[i].x*a[i].y)%mod;
            add1( pos ,a[i].x);
            add2( pos ,tmp);
        }else{

            int L = getblock(a[i].x);       //获取下标x所在的块
            int R = getblock(a[i].y);       //获取下标y所在的块

            //printf("2### L :%d , R : %d\n",L,R);
            //printf(" Sum L : %lld , Sum R : %lld\n",sum1(L),sum1(R));
            ll t1,t2,t3;
            t1 = t2 = t3 = 0;

            t1 = ( (sum1(L) - a[i].x+1+mod)%mod * b[L] )%mod; // L所在的块中的个数×b[L]
            t2 = ( (a[i].y - sum1(R-1)+mod)%mod * b[R] )%mod; // R所在的块中的个数×b[R]
            t3 = (sum2(R-1) - sum2(L) + mod )%mod;            // 中间部分

            ll ans = (t1 + t2 + t3) %mod;
            printf("%lld\n",ans);
        }
    }
    return 0;

}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值