洛谷Oj-P2709 小B的询问-莫队

11人阅读 评论(0) 收藏 举报
分类:

问题描述:
小B有一个序列,包含N个1~K之间的整数。他一共有M个询问,每个询问给定一个区间[L..R],求ki=1c(i)2的值,其中i的值从1到K,其中c(i)表示数字i在[L..R]中的重复次数。小B请你帮助他回答询问。
AC代码:

struct query
{
    int l;
    int r;
    int id;//第id个查询
    int b;//块的编号
    long long ans;//答案可能会爆int
};
query q[50010];
int a[50010];//原数组
int n,m,k;//k其实没啥用
int curL,curR;
int cnt[50010];
long long sum;//求题目中要求的和
bool cmp1(const query &a,const query &b)//分块
{
    if(a.b == b.b)
        return a.r < b.r;
    return a.b < b.b;
}
bool cmp2(const query &a,const query &b)//对查询重新排序
{
    return a.id < b.id;
}
void add(int id)
{
    sum += 2 * cnt[a[id]] + 1;//加上贡献
    cnt[a[id]]++;//注意与上句的顺序不能颠倒
    return;
}
void del(int id)
{
    sum += -2 * cnt[a[id]] + 1;//减去损失
    cnt[a[id]]--;//注意与上句的顺序不能颠倒
    return;
}
inline void solve(query &q)//莫队
{
    int l = q.l;
    int r = q.r;
    while(curL < l)
        del(curL++);
    while(curL > l)
        add(--curL);
    while(curR < r)
        add(++curR);
    while(curR > r)
        del(curR--);
    q.ans = sum;
}
int main()
{
    cin >> n >> m >> k;
    int sz = sqrt(n);//每个块的大小
    for(int i = 1; i <= n; ++i)
        scanf("%d",&a[i]);
    for(int i = 1; i <= m; ++i)//离线处理每次查询
    {
        scanf("%d%d",&q[i].l,&q[i].r);
        q[i].b = q[i].l / sz;//块的编号
        q[i].id = i;
    }
    sort(q + 1,q + m + 1,cmp1);//分块
    //初始化左右指针
    curL = 1;
    curR = 0;
    for(int i = 1; i <= m; ++i)
    {
        //sum = 0;//重置是不对的,因为都说了莫队要利用历史信息
        solve(q[i]);
    }
    sort(q + 1,q + m + 1,cmp2);//恢复查询顺序
    for(int i = 1; i <= m; ++i)//输出
        cout << q[i].ans << endl;
    return 0;
}

解决方法:
c(i)的定义我们可以判断这道题需要用莫队来求解,抱着试一试的心态写了一发无脑求答案的莫队,然后大部分点都T了。数据规模为1<=N、M、K<=50000,极限情况下有50000次查询,每次查询都要求50000次和,那时间上自然是过不去的。
我是这样求答案的:

long long sigma()
{
    long long sum = 0;
    for(int i = 1; i <= k; ++i)//从1到k
        sum += cnt[i] * cnt[i];
    return sum;
}

其实sum完全没有必要每次都重算,对sum的修改可以在add和del函数中实现
对于某数a,它从出现b次增加到了b+1次,之前它对sum的贡献为b2,现在它对答案的贡献为(b+1)2=b2+2b+1,贡献增加了2b+1,所以在写add函数时只需要将增加的贡献加到sum上即可,出现次数减少的情况同上。

查看评论

DOM4J 使用简介

Dom4j 使用简介 作者:冰云 icecloud(AT)sina.com 时间:
  • icecloud
  • icecloud
  • 2003-12-17 23:58:00
  • 9226

洛谷 1972 莫队

洛谷 1972 莫队算法 传送门 其实这道题的标答应该是树状数组或者线段树吧,,,如果我没记错的话曾经是做过类似题目的,,, 然而,,然而,, 好吧开始正题,这道题目我选择的是莫队算法,引用一下某...
  • ctsnevermore
  • ctsnevermore
  • 2016-11-08 11:21:25
  • 132

HH的项链 洛谷1972 莫队 c++

题目描述HH 有一串由各种漂亮的贝壳组成的项链。HH 相信不同的贝壳会带来好运,所以每次散步完后,他都会随意取出一段贝壳,思考它们所表达的含义。HH 不断地收集新的贝壳,因此,他的项链变得越来越长。有...
  • jpwang8
  • jpwang8
  • 2017-02-15 21:19:16
  • 186

洛谷2709 小B的询问(莫队)

题面题目描述小B有一个序列,包含N个1~K之间的整数。他一共有M个询问,每个询问给定一个区间[L..R],求Sigma(c(i)^2)的值,其中i的值从1到K,其中c(i)表示数字i在[L..R]中的...
  • qq_30974369
  • qq_30974369
  • 2017-08-17 16:17:46
  • 129

【模板】带修改莫队 (模板题:洛谷P1903数颜色)

带修改莫队讲解 ~阅前提示: 拥有普通莫队的基础知识;理解莫队的思想; ~简介: 莫队支持的是离线操作,普通莫队只支持查询操作; 而带修改莫队还支持单点修改操作。 ~原理: 普通莫队每一个...
  • zj_yuneng
  • zj_yuneng
  • 2017-05-25 18:38:25
  • 376

BZOJ 3585 mex 莫队算法+分块

题目大意:给定一个长度为n的数组,m次询问某个区间内的mex值 怒写莫队233 将权值分成√n块,记录每个权值的出现次数以及每块内有多少权值出现过 修改O(1)即可完成 查询时首先扫一遍找到第一...
  • PoPoQQQ
  • PoPoQQQ
  • 2015-02-01 22:48:03
  • 2615

BZOJ4241 历史研究 (分块 回滚莫队-教程向)

题目大意给定一个长度为nn的序列,并提出qq个询问,每次询问要求回答区间 [l,r][l,r] 内所有的权值与其出现次数的积的最大值。题解看见这种xjb询问的题当然是要下意识地用分块来搞一搞的,又因为...
  • qq_33330876
  • qq_33330876
  • 2017-06-20 23:01:45
  • 389

【bzoj2120】分块/带修改莫队板子

昨晚梦到和wtt在学校里抓可爱的小鱼?然后被于校长宣布留级?最后我一通电话+巧舌如簧(划)就让他撤回了决定.......
  • Blue_CuSO4
  • Blue_CuSO4
  • 2017-09-12 14:19:55
  • 224

莫队分块算法模板[BZOJ2038]

#include #include #include #include #include #include #include #include #include #include #include #...
  • snowy_smile
  • snowy_smile
  • 2015-10-28 09:27:01
  • 857

洛谷P1972:[SDOI2009]HH的项链(莫队/线段树)

题目传送门:https://www.luogu.org/problem/show?pid=1972 分析:本题有很多种做法,有O(n*log(n))的线段树,也有O(n*sqrt(n))的莫队。线段树...
  • KsCla
  • KsCla
  • 2017-04-18 13:41:55
  • 454
    个人资料
    持之以恒
    等级:
    访问量: 2万+
    积分: 2340
    排名: 1万+
    最新评论