POJ 3067 Japan (树状数组 + 逆序数)

xiaoxiao2021-02-28  63

思路:

复习一发树状数组。树状数组的核心思想:求当前插入的所有的数中,小于某个数(位置)的数的和(个数)是多少。通过这个也可以很快的求出一个序列的逆序数。本题问的是十字路口(crossing)的数量,通过画图我们可以得知当我们按照这个二分图的左边按序号顺序来连线的话,假设我们当前左边的序号为i,那么当处理到左边序号i+1时,我们发现,只要这个当前的y小于之前的某个y,那么即对结果贡献+1。而这样的模式恰好是统计逆序数的过程。注意,当处理某个左边的点时,如果它连有多边,它自己不应该和自己有crossing,所以排序时注意判断一下。

AC代码:

#include <iostream> #include <cstdio> #include <algorithm> #include <string.h> #include <vector> typedef long long int lli; using namespace std; struct node{ int x,y; }a[1001000]; int c[20000]; bool cmp (const node &a,const node &b){ if(a.x == b.x){ return a.y < b.y; } return a.x < b.x; } int n,m;lli k; void update(int i,int v){ while(i <= m){ c[i] += v; i += i&(-i); } } int sum(int i){ int sum = 0; while(i > 0){ sum += c[i]; i -= i&(-i); } return sum; } int main(){ int t; cin>>t; int nca = 0; while(t--){ nca++; memset(c,0,sizeof(c)); scanf("%d%d%lld",&n,&m,&k); for(int i = 0;i<k;i++){ scanf("%d%d",&a[i].x,&a[i].y); } sort(a,a+k,cmp); lli ans = 0; for(int i = 0;i < k;i++){ update(a[i].y,1); ans += (i+1 - sum(a[i].y)); } printf("Test case %d: %lld\n",nca,ans); } return 0; }
转载请注明原文地址: https://www.6miu.com/read-50668.html

最新回复(0)