题解 P1966 【火柴排队】

xzyxzy

2017-07-03 10:55:27

Solution

这道题折腾了一个晚上,最后终于弄出来了 思路见代码最后的注释段 然后至于逆序对的个数怎么求,模拟一下就好了 代码还是很友好的 注意一个点,就是归并排序的时候统计逆序对的个数不是++,而是+=mid-左指针+1(查了我半个多小时,改了就对了) ```cpp #include<iostream> #include<cstdio> #include<algorithm> #include<cstdlib> using namespace std; struct huochai{ int h;//高度 int num;//序号 }a[100001],b[100001]; int n; int read() { char ch=getchar(); while(ch<'0'||ch>'9') ch=getchar(); int h=0; while(ch<='9'&&ch>='0') { h=h*10+ch-48; ch=getchar(); } return h; } int my(const huochai&a,const huochai&b) { if(a.h<b.h) return 1; else return 0; } int c[100001],r[100001],tot=0; void merge_sort(int left,int right) { if(left==right) return; int mid=(right+left)/2,x=left,y=mid+1,t=left; merge_sort(left,mid); merge_sort(mid+1,right); while(x<=mid&&y<=right) { if(r[x]>r[y]) { tot+=mid-x+1;//重点!!! tot%=99999997; c[t++]=r[y++]; } else c[t++]=r[x++]; } while(x<=mid) c[t++]=r[x++]; while(y<=right) c[t++]=r[y++]; for(int w=left;w<=right;w++) r[w]=c[w]; } int main() { n=read(); for(int i=1;i<=n;i++) a[i].h=read(); for(int i=1;i<=n;i++) a[i].num=i; for(int i=1;i<=n;i++) b[i].h=read(); for(int i=1;i<=n;i++) b[i].num=i; sort(a+1,a+n+1,my); sort(b+1,b+n+1,my); for(int i=1;i<=n;i++) r[a[i].num]=b[i].num; //归并求逆序对 merge_sort(1,n); cout<<tot; return 0; } //本题思路: //先来证明一个公式:若a1>a2且b1>b2,则有(a1-b1)^2+(a2-b2)^2<(a2-b1)^2+(a1-b2)^2 // 当然这个公式很容易证,拆开就好了 // 然后运用这个公式,发现为保证火柴距离最小,两列火柴对应的两根火柴在各列中高度的排名应该相同 //再来定义一个r数组,使得r[a[i].num]=b[i].num,则可以很惊奇地发现,交换次数即为r数组中逆序对的个数,此题得解 ```