首页 > 其他分享 >洛谷题单指南-二叉堆与树状数组-P5677 [GZOI2017] 配对统计

洛谷题单指南-二叉堆与树状数组-P5677 [GZOI2017] 配对统计

时间:2024-11-19 16:20:17浏览次数:1  
标签:val P5677 int 洛谷题 id 端点 区间 配对 GZOI2017

原题链接:https://www.luogu.com.cn/problem/P5677

题意解读:所谓好的配对,通过分析公式∣ax−ay∣≤∣ax−ai∣(i≠x),可以得知就是一个ax与其差的绝对值最小的形成的配对,在数轴上就是距离ax最近的点ay,配对是下标(x,y),给定若干个区间[l,r],每个区间的配对数*区间编号的累加。

解题思路:

1、求出所有的好配对

先将所有数按值从小到大排序,同时保留其索引编号struct Node{int val, id;} a[N]

对于每一个数,好配对的另一半要么是左边相邻的数,要么是右边相邻的数,取相差更小的,如果相差一样则都是好配对

特别的,第一个数和最后一个数都只有一个好配对

把得到的好配对(x ,y)都存入一个结构体数组struct Pair{int x, y;} p[2 * N]

2、计算一个区间里的好配对数

要计算区间[l, r]内的好配对数,暴力的方法是直接统计出(x,y)在区间内的配对个数,时间复杂度是O(n)

3、计算所有区间里的好配对数 * 区间编号,并累加

上述方法,m个区间需要O(n * m)

既然是统计区间里配对的个数,能否利用前缀和进行优化?答案是肯定的!

第一步:先对所有好配对按右端点y值从小到大排序

第二步:对所有询问按区间右端点r值从小到大排序

第三步:遍历所有询问,对于每一个询问[l,r],将所有右端点不超过r的配对加入树状数组tr[],对所有符合要求好配对执行树状数组修改操作add(x, 1)

(注:tr[i]表示左端点在i ~ i - lowbit(i) + 1范围内所有好配对数,可以认为原数组s[i]表示左端点i的好配对数)

第四步:计算[l,r]范围好配对数,即当前所有s[i] (i >= l)之和,用树状数组操作即: 当前已加入树状数组的所有配对数 - sum(l - 1)

第五步:区间好配对数还要记得乘以查询区间的编号,查询区间可以用结构体数组保存

100分代码:

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;
const int N = 300005;

int n, m;
//所有数和对应编号
struct Node
{
    int val, id;
    bool operator < (const Node &b) const &
    {
        return val < b.val;
    }
} a[N];
//所有好配对
struct Pair
{
    int x, y;
    bool operator < (const Pair &b) const &
    {
        return y < b.y;
    }
} p[2 * N];
int cnt;
void addPair(int x, int y) //添加配对时,总是把小的作为左边界,大的作为右边界,方便后续的查询
{
    p[++cnt].x = min(x, y);
    p[cnt].y = max(x, y);
}
//所有询问和对应编号
struct Ques
{
    int l, r, id;
    bool operator < (const Ques &b) const &
    {
        return r < b.r;
    }
} q[N];
//树状数组,tr[i]表示左端点是i-lowbit(i)+1 ~ i的好配对个数
int tr[N];

int lowbit(int x)
{
    return x & -x;
}

void add(int x)
{
    for(int i = x; i <= n; i += lowbit(i)) tr[i] += 1;
}

int sum(int x)
{
    int res = 0;
    for(int i = x; i; i -= lowbit(i)) res += tr[i];
    return res;
}

int main()
{
    cin >> n >> m;
    if(n == 1) 
    {
        cout << 0;
        return 0;
    }
    for(int i = 1; i <= n; i++)
    {
        cin >> a[i].val;
        a[i].id = i;
    }
    //按元素值从小到大排序
    sort(a + 1, a + n + 1);
    //计算好配对
    addPair(a[1].id, a[2].id);
    addPair(a[n].id, a[n - 1].id);
    for(int i = 2; i < n; i++)
    {
        int dis1 = a[i].val - a[i - 1].val;
        int dis2 = a[i + 1].val - a[i].val;
        if(dis1 < dis2) addPair(a[i].id, a[i - 1].id);
        else if(dis1 > dis2) addPair(a[i].id, a[i + 1].id);
        else  addPair(a[i].id, a[i - 1].id), addPair(a[i].id, a[i + 1].id);
    }
    //好配对按右端点从小到大排序
    sort(p + 1, p + cnt + 1);
    //读入询问
    for(int i = 1; i <= m; i++)
    {
        cin >> q[i].l >> q[i].r;
        q[i].id = i;
    }
    //询问按右端点从小到大排序
    sort(q + 1, q + m + 1);
    //计算答案
    LL ans = 0;
    int j = 0;
    for(int i = 1; i <= m; i++)
    {
        //对于每一个询问q[i],将右端点小于等于q[i].r的好配对数加入树状数组
        while(j + 1 <= cnt && p[j + 1].y <= q[i].r)
        {
            add(p[++j].x);
        }
        ans += 1ll * (j - sum(q[i].l - 1)) * q[i].id;
    }
    cout << ans;
    return 0;
}

 

标签:val,P5677,int,洛谷题,id,端点,区间,配对,GZOI2017
From: https://www.cnblogs.com/jcwy/p/18554794

相关文章

  • 洛谷题单指南-二叉堆与树状数组-P1908 逆序对
    原题链接:https://www.luogu.com.cn/problem/P1908题意解读:求逆序对,前面介绍过归并排序的做法,参考:https://www.cnblogs.com/jcwy/p/184077,这里介绍树状数组的做法。解题思路:设数组a[n]里的整数只包括1~n,显然对于此题,可以通过离散化得到这样的数组。要计算逆序对,就是要计算对于......
  • 小寄巧——给洛谷题单快速生成一份目录
    以此题单为例,首先我们在浏览器中打开,F12切换到Console,输入document.querySelectorAll(".titlea"),然后复制返回的所有内容,粘贴到VSCode里,内容大致如下:NodeList(15)[a.title.color-default,a.title.color-default,a.title.color-default,a.title.color-default,a.title......
  • 洛谷题单指南-二叉堆与树状数组-P3368 【模板】树状数组 2
    原题链接:https://www.luogu.com.cn/problem/P3368题意解读:树状数组应用-区间修改,单点求值解题思路:设原数组为s[N],其差分数组为a[N]操作一:区间修改要对s[x]~s[y]每个数增加k,相当于对a[x]加k,对a[y+1]减k,O(n)的操作变成了O(1)的操作,利用树状数组tr[N]的add(x,k),add(y+......
  • 洛谷题单指南-二叉堆与树状数组-P3374 【模板】树状数组 1
    原题链接:https://www.luogu.com.cn/problem/P3374题意解读:树状数组模版:单点修改,区间求值。解题思路:树状数组-BinaryIndexTree可以动态维护一组数,可以O(logn)的修改一个数,也可以O(logn)的计算一段区间的和。思考一下朴素做法:如何修改一个数,计算区间和?如果是常规数组,修改操作......
  • 洛谷题单 算法2-2 常见优化技巧
    洛谷题单算法2-2常见优化技巧单调栈单调栈最经典的应用,就是在一个数列里寻找距离元素最近的比其大/小的元素位置。模板题,寻找每个元素右边第一个比它大的元素下标。stack<int>s;for(inti=n;i>=1;i--){while(s.size()&&a[s.top()]<=a[i])s.pop();f[i]=s.......
  • 洛谷题单指南-二叉堆与树状数组-P1878 舞蹈课
    原题链接:https://www.luogu.com.cn/problem/P1878题意解读:n个男女排列一行,每人舞蹈技术是ai,每次找到相邻男女舞蹈技术差值绝对值最小的一对出列,输出每对出列的人员编号。解题思路:设初始有8人编号为:12345678将12,23,34,45,56,67,78中是男女的配对编号以及a......
  • 洛谷题单103数组题解||by红糖
    P1428小鱼比可爱题目描述人比人,气死人;鱼比鱼,难死鱼。小鱼最近参加了一个“比可爱”比赛,比的是每只鱼的可爱程度。参赛的鱼被从左到右排成一排,头都朝向左边,然后每只鱼会得到一个整数数值,表示这只鱼的可爱程度,很显然整数越大,表示这只鱼越可爱,而且任意两只鱼的可爱程度可能一样......
  • 洛谷题单指南-二叉堆与树状数组-P2085 最小函数值
    原题链接:https://www.luogu.com.cn/problem/P2085题意解读:有n个函数,函数中x取值>=1,计算所有函数能得到的值中最小的m个。解题思路:函数中x取值是>=1的整数,因此每个函数的值是f(1),f(2),f(3)....,是一个递增序列,题目本质上是要从n个递增序列中找到前m个最小的数。首先,对所有函数......
  • 洛谷题单入门1顺序结构(C语言版)
    【入门1】顺序结构Hello,World!#include<stdio.h>intmain(){printf("Hello,World!");return0;}输出字符菱形#include<stdio.h>intmain(){printf("*\n");printf("***\n");printf("*****\n&q......
  • 洛谷题单指南-二叉堆与树状数组-P1168 中位数
    原题链接:https://www.luogu.com.cn/problem/P1168题意解读:中位数就是位于中间的数,前1个数的中位数是第1个,前3个数的中位数是第2个,前5个数的中位数的第3个...以此类推。所以,此题本质上就是动态维护一组数,每1/3/5...等奇数个取第k小的数,取一次后k++。解题思路:要动态维护数据,且每......