ICPC2021河南省赛 J.甜甜圈(树状数组、思维)

  • 题目:甜甜圈
  • 解析:因为每次吃掉的甜甜圈必须是最大甜度的,所以假设第x(1 <= x <= 2)根柱子上的第i个甜甜圈为当前最甜的,那么该根柱子上从顶端到第i-1个甜甜圈都必须转移到另一个柱子上,所以可以通过树状数组对区间进行维护.
    1. 首先,因为甜甜圈是通过两根柱子顶端进行转移的,为了更好维护甜甜圈的转移过程,我们可以将两根柱子的顶端进行拼接(自行进行想象),然后该位置上有甜甜圈则为1,否则为0,接着将甜甜圈按甜度从大到小排序;
    2. 我们选第一根或者第二根柱子的顶部位置p作为一个分界点,因为甜甜圈转移方式总是从顶部转移的,取出当前最甜的甜甜圈的位置a[i].pos:
      1. a[i].pos <= p; 说明该甜甜圈在第一根柱子,那么吃掉该甜甜圈的步数肯定是将(a[i].pos , p]这个区间的甜甜圈给转移,这时候用树状数组维护即可
      2. a[i].pos < p; 说明该甜甜圈在第二根柱子,那么吃掉该甜甜圈的步数肯定是将第二根柱子顶端到a[i].pos之前的所有甜甜圈转移到第一根柱子上,依然用树状数组维护即可
  • 代码:
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1e5 + 5;
int n, m;
int c[N];
long long t = 0;
struct Node
{
    int val, pos;
}a[N];
bool cmp(Node a, Node b)
{
    return a.val > b.val;
}
int lowbit(int x)
{
    return x & (-x);
}
void add(int x, int k)
{
    while(x <= n + m)
    {
        c[x] += k;
        x += lowbit(x);
    }
}
int query(int x)
{
    int res = 0;
    while(x)
    {
        res += c[x];
        x -= lowbit(x);
    }
    return res;
}
int main()
{
    cin >> n >> m;
    //将第一根柱子上的甜甜圈顶部与第二根柱子上甜甜圈顶部相连
    for(int i = n; i >= 1; i--)
    {
        cin >> a[i].val;
        a[i].pos = i;
        add(i, 1);
    }
    for(int i = n + 1; i <= n + m; i++)
    {
        cin >> a[i].val;
        a[i].pos = i;
        add(i, 1);
    }
    sort(a + 1, a + n + m + 1, cmp);
    int p = n; //选第一根或者第二根柱子的顶部均可
    for(int i = 1; i <= n + m; i++)
    {
        int pos = a[i].pos; //当前甜度最大的甜甜圈位置
        if(pos <= p) //该甜甜圈在第一根柱子
            t += query(p) - query(pos);
        else t += query(pos) - query(p) - 1; //query(p+1)可能越界
        add(pos, -1);
        p = pos;
    }
    cout << t << endl;
    return 0;
}

posted @ 2021-05-31 21:34  ~K2MnO4  阅读(201)  评论(0)    收藏  举报