AcWing 1215. 小朋友排队

原题链接

考察:树状数组 +贪心 or归并排序+贪心

思路:

        首先一个定理:冒泡排序的交换次数 = 逆序对个数.证明: 冒泡排序每次交换减少一个逆序对,当最后逆序对数量 = 0,交换次数>=k. 又因为每次只能减少一个.那么必然可以取到k.对于某一个人i而言,i前面>hi有k个,后面<hi有t个.k+t就是i的交换次数最优解,而无需考虑是否相邻的问题.将每个人的k+t累加,和为逆序对数量*2,因为最优解的交换次数就是逆序对*2(一个逆序对对于两个人来说都+1).所以k+t就是i交换次数的最优解.

        所以计算每个人的k+t即可,可以用归并排序或树状数组.

        如果用归并,要记录每个人的交换前后位置差,不能用int数组记录第i个人交换次数,因为每人的id都在变化.

 1 #include <iostream>
 2 #include <cstring>
 3 #include <algorithm>
 4 using namespace std;
 5 typedef long long LL;
 6 const int N = 100010;
 7 int n;
 8 LL ans;
 9 struct SS{
10     int h,id,cnt;
11 }ss[N],b[N];
12 void merge(int l,int r)
13 {
14     if(l==r) return;
15     int mid = l+r>>1;
16     merge(l,mid); merge(mid+1,r);
17     int i = l,j = mid+1,k= 0;
18     while(i<=mid&&j<=r)
19     {
20         if(ss[i].h<=ss[j].h) b[k++] = ss[i++];
21         else
22             b[k++] = ss[j++];
23     }
24     while(i<=mid) b[k++] = ss[i++];
25     while(j<=r) b[k++] = ss[j++];
26     for(int i=l;i<=r;i++) 
27     {
28         ss[i] =b[i-l];
29         ss[i].cnt+=abs(ss[i].id-i);
30         ss[i].id = i;
31     }
32 }
33 int main()
34 {
35     scanf("%d",&n);
36     for(int i=1;i<=n;i++)
37     {
38         scanf("%d",&ss[i].h);
39         ss[i].id = i;
40     }
41     merge(1,n);
42     for(int i=1;i<=n;i++) ans +=(LL)(1+ss[i].cnt)*ss[i].cnt/2;
43     printf("%lld\n",ans);
44     return 0;
45 }
归并

        树状数组,记录每个人前面>hi的有几个,答案是summax-sumhi.树状数组只能求前面<=hi的.所以可以利用前缀和.

 1 #include <iostream>
 2 #include <cstring>
 3 #include <algorithm>
 4 using namespace std;
 5 typedef long long LL;
 6 const int N = 100010,M = 1000010;
 7 int n,h[N],tr[M],cnt[N];
 8 LL ans;
 9 int lowbit(int x)
10 {
11     return x&-x;
12 }
13 void add(int idx,int x)
14 {
15     for(int i=idx;i<=M-10;i+=lowbit(i)) tr[i]+=x;
16 }
17 int query(int x)
18 {
19     int res= 0;
20     for(int i=x;i;i-=lowbit(i)) res+=tr[i];
21     return res;
22 }
23 int main()
24 {
25     scanf("%d",&n);
26     for(int i=1;i<=n;i++) scanf("%d",&h[i]);
27     for(int i=1;i<=n;i++)
28     {
29         cnt[i]+=query(M-10)-query(h[i]);
30         add(h[i],1);
31     }
32     memset(tr,0,sizeof tr);
33     for(int i=n;i>=1;i--)
34     {
35         cnt[i]+=query(h[i]-1);
36         add(h[i],1);
37     }
38     for(int i=1;i<=n;i++) ans+=(LL)cnt[i]*(cnt[i]+1)/2;
39     printf("%lld\n",ans);
40     return 0;
41 }
树状数组

 

posted @ 2021-03-04 08:54  acmloser  阅读(74)  评论(0编辑  收藏  举报