codefroce D. Powerful array[初识块状数组]

因为是初始所以,只能先用别人的分析。囧。。。

题目:

给定一个数列:A1, A2,……,An,定义Ks为区间(l,r)中s出现的次数。

t个查询,每个查询l,r,对区间内所有a[i],求sigma(K^2*a[i])

离线+分块

将n个数分成sqrt(n)块。

对所有询问进行排序,排序标准:

1. Q[i].left /block_size < Q[j].left / block_size (块号优先排序)

2. 如果1相同,则 Q[i].right < Q[j].right (按照查询的右边界排序)

问题求解:

从上一个查询后的结果推出当前查询的结果。(这个看程序中query的部分)

如果一个数已经出现了x次,那么需要累加(2*x+1)*a[i],因为(x+1)^2*a[i] = (x^2 +2*x + 1)*a[i],x^2*a[i]是出现x次的结果,(x+1)^2 * a[i]是出现x+1次的结果。

时间复杂度分析:

排完序后,对于相邻的两个查询,left值之间的差最大为sqrt(n),则相邻两个查询左端点移动的次数<=sqrt(n),总共有t个查询,则复杂度为O(t*sqrt(n))。

又对于相同块内的查询,right端点单调上升,每一块所有操作,右端点最多移动O(n)次,总块数位sqrt(n),则复杂度为O(sqrt(n)*n)。

right和left的复杂度是独立的,因此总的时间复杂度为O(t*sqrt(n)  +  n*sqrt(n))。

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;

typedef long long LL;
const int V = 200000 + 10;
const int MAXN = 1000000 + 10;

struct node{
   int l,r,id;
}query[V];

int n,t,num[V],L,R,sum[MAXN];
LL ans[MAXN],now;

bool cmp(node a,node b){
    int m = sqrt(1.0*n);    //最好的理论值
    if(a.l / m != b.l / m){
        return a.l < b.l;
    }
    return a.r < b.r;
}

void modify(int l,int r){

    while(L < l){ //左区间不包含
        sum[num[L]]--;
        now -= num[L] * (sum[num[L]] << 1 | 1);
        L++;
    }

    while(R > r){  //右区间不包含
        sum[num[R]]--;
        now -= num[R] * (sum[num[R]] << 1 | 1);
        R--;
    }

    while(L > l){  //上一区间的左区间包含在当前区间里
        L--;
        now += num[L] * (sum[num[L]] << 1 | 1);
        sum[num[L]]++;

    }

    while(R < r){  //上一区间的右区间包含在当前区间里
        R++;
        now += num[R] * (sum[num[R]] << 1 | 1);
        sum[num[R]]++;
    }
}

int main()
{
    while(~scanf("%d%d",&n,&t)){
        for(int i = 1;i <= n;++i){
            scanf("%d",&num[i]);
        }

        for(int i = 1;i <= t;++i){
            scanf("%d%d",&query[i].l,&query[i].r);
            query[i].id = i;
        }

        sort(query+1,query + t + 1,cmp);
        now = L = R = 0;
        memset(sum,0,sizeof(sum));

        for(int i = 1;i <= t;++i){
            modify(query[i].l,query[i].r);
            ans[query[i].id] = now;
        }

        for(int i = 1;i <= t;++i){
            printf("%I64d\n",ans[i]);
        }
    }
    return 0;
}

时间: 2024-10-14 11:58:21

codefroce D. Powerful array[初识块状数组]的相关文章

D. Powerful array 莫队算法或者说块状数组 其实都是有点优化的暴力

莫队算法就是优化的暴力算法.莫队算法是要把询问先按左端点属于的块排序,再按右端点排序.只是预先知道了所有的询问.可以合理的组织计算每个询问的顺序以此来降低复杂度. D. Powerful array 典型的莫队算法题 1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cmath> 5 #include <algorithm> 6 #include

uva 12003 Array Transformer (块状数组)

大白书上的393页. 一直在原数组上乱搞.其实要用另外一个数组记录块. 原数组是不能变的. 注意好原数组和块数组的关系,细心一点处理边界.还是不难的. #include <cstdio> #include <iostream> #include <algorithm> #include <cstring> #define maxn 300005 #define SIZE 600 using namespace std; int a[maxn]; int bl

HNOI2010弹飞绵羊(块状数组)

不得不说块状数组好神奇的啊!这道题的标签可是splay的启发是合并(什么高大上的东西),竟然这么轻松的就解决了! var x,y,i,j,tot,n,m,ch:longint; f,k,l,bl,go:array[0..200100] of longint; procedure init; begin readln(n); x:=trunc(sqrt(n));j:=x; for i:=1 to n do begin if j=x then begin j:=1;inc(tot);l[tot]:=

codeforces 86D D. Powerful array(莫队算法)

题目链接: D. Powerful array time limit per test 5 seconds memory limit per test 256 megabytes input standard input output standard output An array of positive integers a1, a2, ..., an is given. Let us consider its arbitrary subarray al, al + 1..., ar, wh

javascript中的稀疏数组(sparse array)和密集数组

学习underscore.js数组相关API的时候,遇到了sparse array这个东西,以前没有接触过. 这里学习下什么是稀疏数组和密集数组. 什么是密集数组呢?在java和C语言中,数组是一片连续的存储空间,有着固定的长度.加入数组其实位置是address,长度为n,那么占用的存储空间是address[0],address[1],address[2].......address[n-1].即数组元素之间是紧密相连的,不存在空隙.如下的js代码创建的就是一个密集数组 var data = [

HDU 3854 Glorious Array(树状数组)

题意:给一些结点,每个结点是黑色或白色,并有一个权值.定义两个结点之间的距离为两个结点之间结点的最小权值当两个结点异色时,否则距离为无穷大.给出两种操作,一种是将某个结点改变颜色,另一个操作是询问当前距离小于K的结点有多少对,K是一个定值. 思路:先求最初时候小于k的结点有多少对,然后每次改变颜色的时候,统计该点左侧和右侧各有多少同色和异色的结点(这一步使用树状数组),分别处理就行.另外需要预处理离某个结点最近的两个距离小于K的结点的位置. 代码写的略乱. #include<cstdio> #

块状数组

定义: 块状数组是基于分块思想的数据结构,较基于分治思想的数据结构如线段树.平衡树等效率较低,但通用性更强.在块状数组的基础上加以扩展,就可以得到块状链表. 原理: 普通数组在处理一些区间问题时,复杂度通常会退化至O(n).一个朴素的想法就是将这个数组分为若干个子区间,同时维护这些子区间的统计值,如区间和.区间最值等.对于某个子区间,如果操作区间覆盖子区间,则在整体上进行修改并打标记.如果操作区间部分覆盖子区间,则将该块标记下放,对区间中被覆盖部分的元素进行暴力操作. 设数组长度为n,将其分为s

Java 反射Array动态创建数组

Java 反射Array动态创建数组 @author ixenos 注:java.lang.reflect.Array 是个反射工具包,全是静态方法,创建数组以多维数组为基准,一维数组只是特殊实现 创建一个具有指定的组件类型和长度的新数组(一维数组) newInstance public static Object newInstance(Class<?> componentType, int length) throws NegativeArraySizeException 创建一个具有指定

Big String 块状数组(或者说平方分割)

Big String 给一个字符串,长度不超过 106,有两种操作: 1. 在第 i 个字符的前面添加一个字符 ch 2. 查询第 k 个位置是什么字符 操作的总数不超过 2000 如果直接模拟的话,移动到后面的数据量太大.我们分块的话,就可以优化,减少移动的量.  很典型的块状数组.块状数组的next指向的是一块,而不是一个.这里用整数代替了指针. 每一个块就是一个对象.这题用面向对象编程. 1 #include <iostream> 2 #include <cstdio> 3