[题解]洛谷P1966 火柴排队
对于两个队列a,b,求怎么每次只序列中交换相邻两个数使∑(a[i]-b[i])2 最小,输出次数对99,999,997取余
展开得:∑a[i]2+b[i]2 -∑2a[i]b[i]
而∑a[i]2+b[i]2 是不变的,所以只要最大化∑2a[i]b[i]即可。
首先可以确定,对两个序列的变化完全可以集中到一个序列里完成
一个贪心策略:将a中最大的数和b中最大的数配对,第二大的和第二大的……
然后把a中每一个数的标记设为他应该对应的b[i]的i,设这个标记序列为c
然后对c从小到大排序的过程中统计逆序对即可
代码:
#include<cstdio> #include<algorithm> #include<cstring> #include<iostream> using namespace std; const int mod = 99999997,MAXN = 1000010; int n,c[MAXN]; struct node{ int n,p,tag; }a[MAXN],b[MAXN]; int cmp1(node lxl,node chen_zhe){ return lxl.n<chen_zhe.n; } int cmp2(node lxl,node chen_zhe){ return lxl.p<chen_zhe.p; } int temp[MAXN]; long long ans=0; void merge_sort(int l,int r){ if(r-l>1){ int m=(l+r)/2; merge_sort(l,m); merge_sort(m,r); int p1=l,p2=m,i=l; while(p1<m||p2<r){ if(p2>=r||(p1<m&&c[p1]<=c[p2]))temp[i++]=c[p1++]; else { temp[i++]=c[p2++]; ans+=(m-p1)%mod; } } for(i=l;i<r;i++)c[i]=temp[i]; } } int main(){ scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%d",&a[i].n); a[i].p=i; } for(int i=1;i<=n;i++){ scanf("%d",&b[i].n); b[i].p=i; } sort(a+1,a+n+1,cmp1); sort(b+1,b+n+1,cmp1); for(int i=1;i<=n;i++) a[i].tag=b[i].p; sort(a+1,a+n+1,cmp2); for(int i=1;i<=n;i++) c[i]=a[i].tag; merge_sort(1,n+1); printf("%lld",ans%mod); return 0; }
本篇文章为SHINE_GEEK原创,转载请注明来源!
-------------------------------------
签名:自己选的路,跪着也要走完;理想的实现,需要不懈奋斗!
-------------------------------------
written_by:SHINE_GEEK
blog_addr:www.cnblogs.com/sjrb
-------------------------------------
签名:自己选的路,跪着也要走完;理想的实现,需要不懈奋斗!
-------------------------------------