• 博客园logo
  • 会员
  • 周边
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • YouClaw
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录

yongchaoD

  • 博客园
  • 联系
  • 订阅
  • 管理

公告

View Post

Codeforces Greetings(逆序对)

题目来源:https://codeforces.com/problemset/problem/1915/F
题意:每个人从起点到终点(终点>起点),每个人的起点和终点都不一样,两个人在同一点相遇时,他们会互相问候一次。会有多少问候?(请注意,一个人仍然可以问候其他人,即使他们已经达到
了他们的最后一点。)。说白了就是问有多少对逆序对(i<j && a[i]>a[j])。

思路:离散化+树状数组求逆序对。

  1:因为起点并不是有序的,所以先用一个map容器存起来,键值存起点,实值存终点,map的键值自动排序,这样起点就是有序了。
  2:然后将其离散化,目的是用一个d[]数组:第i大的数字在b[i]下标。比如原序列a[]={5,3,4,2,1},离散化后的d[]数组就是d[]={1,3,2,4,5}。第一大的数字在下标1,第二大 
     的数字在下标3... 
  3:然后d数组每次进一个数组:
      1进入:此时只有一个数字1,sum=0
      3进入:此时有两个数字1,3,比3小的数字只有一个1,sum+=1
      2进入:此时比2小的数字有1,sum=2
      4进入:此时比4小的数字有1,3,2,sum=5
      5进入:此时比5小的数字有1,3,2,4,sum=9
     所以序列{5,3,4,2,1}一共有9个逆序对。

原理是: b数组是按下标从左到右依次进入,而b数组下标i的含义就是第i大的数字,所以数字大的先进,而逆序对就是数字大,但是大的数字下标小于数字小的下标,而b[]数组元素就是第i大的数字的下标,所以进一个数字,就看有没有更早进的数字,且数字小于该数字,有则说明这个数大于你,且下标比你小,这不就满足一对逆序对的要求吗?然后sum++即可。

 问题又来了:根据上面的步骤每一次把一个新的数d[x]放进去之后,都要求比他小的元素有几个,而比他小的元素个数一定是1到d[x]中存在数的个数,也就是[1 , d[x-1]]中有几个数。我们就用树状数组tree[]来维护[1,x]有多少个数字存在。

题解:

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;

const int N=2e5+9;

int t,n,ai,bi,sum=0;
int a[N],d[N],tree[N];

map<int,int>mp;//first是起点

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

bool cmp1(int x,int y){//升序排序
    if(a[x]==a[y]) return x>y;
    return a[x]>a[y];
}

void add(int x){
    while(x<=n){
        tree[x]++;
        x+=lowbit(x);
    }
}

int getsum(int x){
    int ans=0;
    while(x>=1){
        ans+=tree[x];
        x-= lowbit(x);
    }
    return ans;
}

signed main()
{
    scanf("%lld",&t);
    while(t--){
        for(int i=0;i<N;i++){a[i]=0,d[i]=0,tree[i]=0;}//init
        sum=0;
        mp.clear();

        scanf("%lld",&n);
        for(int i=1;i<=n;i++){
            scanf("%lld%lld",&ai,&bi);//ai是下标
            mp[ai]=bi;
        }
        
        int tot=0;
        for(auto x:mp){
            a[++tot]=x.second,d[tot]=tot;
        }

        sort(d+1,d+1+n,cmp1);//第i大的数字在d[i]的下标

        for(int i=1;i<=n;i++){
            add(d[i]);
            sum+=getsum(d[i]-1);
        }
        printf("%lld\n",sum);
    }
    return 0;
}

posted on 2024-07-10 15:27  yongchaoD  阅读(25)  评论(0)    收藏  举报

刷新页面返回顶部
 
博客园  ©  2004-2026
浙公网安备 33010602011771号 浙ICP备2021040463号-3