codechef November Challenge 2014 .Chef and Churu

15 篇文章 0 订阅
3 篇文章 0 订阅
链接地址:here
题意:

N 105)个数,给定 N 个函数,每个函数为序列中第Li 到第 Ri 个数的和,有 Q 105)个询问,有两种类型的操作:

  • 1 x y 把序列中的第 x 个数改为y
  • 2 m n 求第 m 个函数到第n个函数的和
思路:

分块+树状数组
对N个函数进行分块,分块处理出每个块内,序列中第i个数被这个块内的函数覆盖的次数, 然后处理出每个块内函数的和,由于是单点更新,所以在维护每个块函数的和的时候是比较好维护的(因为已经知道了第i个数被这个块内的函数覆盖的次数),然后就是区间查询了,直接分块处理就好了,块内求解的时候就需要用到树状数组去优化了。

分块大法好!1240 ms 跑过,内存消耗太大啊!

代码:
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
#include <iostream>
#include <cmath>
#include <map>
#include <vector>
#include <set>
#include <string>
#define PB push_back
#define FT first
#define SD second
#define MP make_pair
#define INF 0x3f3f3f3f
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int>  P;
const int N=5+1e5,M=400,MOD=7+1e9;
int n, a[N];
P d[N];
int belong[N],l[M],r[M];
int vis[M][N];
ULL sum[N],sumb[N];
void update(int pos,int val)
{
    while(pos<N) {
        sum[pos] += val;
        pos += pos&(-pos);
    }
}
ULL read(int pos)
{
    ULL ans=0;
    while(pos) {
        ans += sum[pos];
        pos -= pos&(-pos);
    }
    return ans;
}
ULL cal(int x)
{
    if(x<1) return 0;
    int b = belong[x];
    ULL ans = 0;
    for(int i = 1;i < b ;i ++){
        ans += sumb[i];
    }
    for(int i = l[b];i <= x;i ++) {
        ans += read(d[i].SD)-read(d[i].FT-1);
    }
    return ans;
}
int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    scanf("%d",&n);
    for(int i = 1;i <= n;i ++) {
        scanf("%d",&a[i]);
        update(i,a[i]);
    }
    int block = sqrt(n);
    for(int i = 1;i <= n;i ++) {
        scanf("%d%d",&d[i].FT,&d[i].SD);
        belong[i] = (i-1)/block + 1;
    }
    int cnt = n/block;
    if(n%block) cnt++;
    for(int i = 1;i <= cnt;i ++)
        l[i] = (i-1)*block + 1,r[i] = i*block;
    r[cnt] = n;
    for(int i = 1;i <= cnt;i ++){
        for(int j = l[i];j <= r[i];j ++) {
            vis[i][d[j].FT] ++;
            vis[i][d[j].SD+1] --;
        }
        for(int j = 1;j <= n;j ++) {
            vis[i][j] += vis[i][j-1];
            sumb[i] += 1ULL*vis[i][j]*a[j];
        }
    }
    int Q;
    scanf("%d",&Q);
    while(Q-- ){
        int tp,x,y;
        scanf("%d%d%d",&tp,&x,&y);
        if(tp == 1) {
            update(x,y-a[x]);
            for(int i = 1;i <= cnt;i ++){
                sumb[i] += 1ULL*vis[i][x]*(y-a[x]);
            }
            a[x] = y;
        }
        else {
            printf("%llu\n",cal(y)-cal(x-1));
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值