HihoCoder 1488 : 排队接水(莫队+树状数组)

描述

有n个小朋友需要接水,其中第i个小朋友接水需要ai分钟。

由于水龙头有限,小Hi需要知道如果为第l个到第r个小朋友分配一个水龙头,如何安排他们的接水顺序才能使得他们等待加接水的时间总和最小。

小Hi总共会有m次询问,你能帮助他解决这个问题吗?

假设3个小朋友接水的时间分别是2,3,4。如果他们依次接水,第一位小朋友等待加接水的时间是2,第二位小朋友是5,第三位小朋友是9。时间总和是16。

输入

第一行一个数T(T<=10),表示数据组数

对于每一组数据:

第一行两个数n,m(1<=n,m<=20,000)

第二行n个数a1...an,表示每个小朋友接水所需时间(ai<=20,000)

接下来m行,每行两个数l和r

输出

对于每次询问,输出一行一个整数,表示答案。

样例输入

1
4 2
1 2 3 4
1 2
2 4

样例输出

4
16

思路:贪心可知,时间小的在前。但是排序是不可能的,需要更高效的方法,注意到ai<=2e5,适合用树状数组记录a[i]的个数前缀和,以及a[i]的前缀和。

可以离线,所以用莫队+树状数组,莫队的话,第一次写这中数学类型的转移,开始还有点抵触,但是拿出笔一划,公式也不难。

对于暴力的公式,即L<=i<=R的所有i的前缀和:

for(i=L;i<=R;i++)
 for(j=L;j<=i;j++)
  sum+=a[j];

那么现在加一个第i=x进去,则对新的 i=x,需要多累加前缀:

for(j=L;j<=x;j++)
  sum+=a[j]; 

对于后面的i>=x,都需要累加一个j=x,即a[x]*后面的个数。

for(i=x;i<=R;i++)
 for(j=L;j<=i;j++)
  sum+=a[j]; 

然后把上面的转化为树状数组的前缀和即可。  两个树状数组,分别记录个数和累加和。

#include<cmath>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
#define ll long long
const int maxn=20000;
ll a[maxn+10],num[maxn+10],cnt,B,l,r,tmp;
ll sum[maxn+10];
struct in{ ll L;ll R;ll id; ll ans;}s[maxn+10];
bool cmp(in x,in y){ if(x.L/B==y.L/B) return x.R/B<y.R/B; return x.L/B<y.L/B; }
bool cmp2(in x,in y){ return x.id<y.id;}
void update(){ cnt=0; memset(num,0,sizeof(num));memset(sum,0,sizeof(sum)); }
void addnum(ll x,ll y) { while(x<=maxn){ num[x]+=y; x+=(-x)&x; } }
void addsum(ll x,ll y) { while(x<=maxn){ sum[x]+=y; x+=(-x)&x; } }
int querynum(int x){ ll res=0; while(x>0){ res+=num[x]; x-=(-x)&x;} return res; }
int querysum(int x){ ll res=0; while(x>0){ res+=sum[x]; x-=(-x)&x;} return res;}
int main()
{

    ll T,n,m,i;ll tsum;
    scanf("%d",&T);
    while(T--){
        update();
        scanf("%lld%lld",&n,&m);
        for(i=1;i<=n;i++) scanf("%lld",&a[i]);
        for(i=1;i<=m;i++) s[i].id=i,scanf("%lld%lld",&s[i].L,&s[i].R);
        B=sqrt(n);
        sort(s+1,s+m+1,cmp);
        l=r=1; tmp=a[1]; addnum(a[1],1); addsum(a[1],a[1]);
        for(i=1;i<=m;i++){
            while(l<s[i].L){
                tsum=querysum(a[l]);
                tmp-=tsum;
                tmp-=(querynum(maxn)-querynum(a[l]))*a[l];
                addnum(a[l],-1);
                addsum(a[l],-a[l]);
                l++;
            }
            while(l>s[i].L){
                l--;
                addnum(a[l],1);
                addsum(a[l],a[l]);
                tsum=querysum(a[l]);
                tmp+=tsum;
                tmp+=(querynum(maxn)-querynum(a[l]))*a[l];
            }
            while(r<s[i].R){
                r++;
                addnum(a[r],1);
                addsum(a[r],a[r]);
                tsum=querysum(a[r]);
                tmp+=tsum;
                tmp+=(querynum(maxn)-querynum(a[r]))*a[r];
            }
            while(r>s[i].R){
                tsum=querysum(a[r]);
                tmp-=tsum;
                tmp-=(querynum(maxn)-querynum(a[r]))*a[r];
                addnum(a[r],-1);
                addsum(a[r],-a[r]);
                r--;
            }
            s[i].ans=tmp;
        }
        sort(s+1,s+m+1,cmp2);
        for(i=1;i<=m;i++) printf("%lld\n",s[i].ans);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/hua-dong/p/8455183.html

时间: 2024-11-09 02:37:30

HihoCoder 1488 : 排队接水(莫队+树状数组)的相关文章

bzoj 3289: Mato的文件管理 莫队+树状数组

3289: Mato的文件管理 Time Limit: 40 Sec  Memory Limit: 128 MB[Submit][Status][Discuss] Description Mato同学从各路神犇以各种方式(你们懂的)收集了许多资料,这些资料一共有n份,每份有一个大小和一个编号.为了防止他人偷拷,这些资料都是加密过的,只能用Mato自己写的程序才能访问.Mato每天随机选一个区间[l,r],他今天就看编号在此区间内的这些资料.Mato有一个习惯,他总是从文件大小从小到大看资料.他先

51nod 1290 Counting Diff Pairs | 莫队 树状数组

51nod 1290 Counting Diff Pairs | 莫队 树状数组 题面 一个长度为N的正整数数组A,给出一个数K以及Q个查询,每个查询包含2个数l和r,对于每个查询输出从A[i]到A[j]中,有多少对数,abs(A[i] - A[j]) <= K(abs表示绝对值). 题解 莫队!//其实我就是搜索"51nod + 莫队"找到的这道题-- 七级算法题! 一道320分! 你值得拥有! 题解就是--用个普通的莫队,加上树状数组来统计符合条件的数个数,就好啦. 当增加/

BZOJ 3289 Mato的文件管理(莫队+树状数组)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=3289 [题目大意] 求静态区间逆序对. [题解] 我们对查询进行莫队操作,对于区间的删改我们可以计算出改变量对于逆序对的贡献, 利用树状数组维护即可. [代码] #include <cstdio> #include <algorithm> #include <cmath> #include <cstring> const int N=50100;

Codeforces 375D Tree and Queries(DFS序+莫队+树状数组)

题目链接  Tree and Queries 题目大意  给出一棵树和每个节点的颜色.每次询问vj, kj 你需要回答在以vj为根的子树中满足条件的的颜色数目, 条件:具有该颜色的节点数量至少为kj. (莫队居然可以过) 首先转DFS序,这样就变成了区间查询. 然后直接套用莫队,求出每次询问状态下的t[],t[k]表示当前区间内拥有k个节点的颜色数量. 然后统计t[k] + t[k + 1], ..., t[MAX]即可,这个过程用树状数组维护. #include <bits/stdc++.h>

BZOJ3289[JZYZOJP2018]: Mato的文件管理 莫队+树状数组+离散化

        描述 Description     Mato同学从各路神犇以各种方式(你们懂的)收集了许多资料,这些资料一共有n份,每份有一个大小和一个编号.为了防止他人偷拷,这些资料都是加密过的,只能用Mato自己写的程序才能访问.Mato每天随机选一个区间[l,r],他今天就看编号在此区间内的这些资料.Mato有一个习惯,他总是从文件大小从小到大看资料.他先把要看的文件按编号顺序依次拷贝出来,再用他写的排序程序给文件大小排序.排序程序可以在1单位时间内交换2个相邻的文件(因为加密需要,不能

BZOJ_3289_Mato的文件管理_莫队+树状数组

Description Mato同学从各路神犇以各种方式(你们懂的)收集了许多资料,这些资料一共有n份,每份有一个大小和一个编号 .为了防止他人偷拷,这些资料都是加密过的,只能用Mato自己写的程序才能访问.Mato每天随机选一个区间[l,r ],他今天就看编号在此区间内的这些资料.Mato有一个习惯,他总是从文件大小从小到大看资料.他先把要看的 文件按编号顺序依次拷贝出来,再用他写的排序程序给文件大小排序.排序程序可以在1单位时间内交换2个相邻的 文件(因为加密需要,不能随机访问).Mato想

3289. Mato的文件管理【莫队+树状数组】

Description Mato同学从各路神犇以各种方式(你们懂的)收集了许多资料,这些资料一共有n份,每份有一个大小和一个编号 .为了防止他人偷拷,这些资料都是加密过的,只能用Mato自己写的程序才能访问.Mato每天随机选一个区间[l,r ],他今天就看编号在此区间内的这些资料.Mato有一个习惯,他总是从文件大小从小到大看资料.他先把要看的 文件按编号顺序依次拷贝出来,再用他写的排序程序给文件大小排序.排序程序可以在1单位时间内交换2个相邻的 文件(因为加密需要,不能随机访问).Mato想

Chika and Friendly Pairs(莫队+树状数组+离散化+预处理上下界)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6534 Chika and Friendly Pairs Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 524288/524288 K (Java/Others)Total Submission(s): 160    Accepted Submission(s): 52 Problem Description Chika gives y

HH的项链 HYSBZ - 1878 (莫队/ 树状数组)

HH有一串由各种漂亮的贝壳组成的项链.HH相信不同的贝壳会带来好运,所以每次散步 完后,他都会随意取出一 段贝壳,思考它们所表达的含义.HH不断地收集新的贝壳,因此他的项链变得越来越长.有一天,他突然提出了一 个问题:某一段贝壳中,包含了多少种不同的贝壳?这个问题很难回答...因为项链实在是太长了.于是,他只 好求助睿智的你,来解决这个问题. Input 第一行:一个整数N,表示项链的长度. 第二行:N个整数,表示依次表示项链中贝壳的编号(编号为0到1000000之间的整数). 第三行:一个整数