[atAGC023F]01 on Tree

考虑这样一个问题——

有若干个$01$序列,将这些序列以任意顺序连接,最小化逆序对数

记其中第$i$个序列有$a_{i}$个$0$和$b_{i}$个$1$,序列内部的逆序对数可以直接统计

此时,仅需统计序列间的逆序对数,进而即按照$\frac{b_{i}}{a_{i}}$从小到大排序(调整法易证)

在此基础上,在每一个节点上维护一个序列(初始仅自身),并同样记录$a_{i}$和$b_{i}$

对于全局最小的$\frac{b_{i}}{a_{i}}$(除根节点外),其必然在父亲加入后立即加入,即可与父亲合并

具体实现中,"最小的$\frac{b_{i}}{a_{i}}$"可以用set维护,树的结构可以用并查集维护

时间复杂度为$o(n\log n)$,可以通过

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define N 200005
 4 #define ll long long
 5 int n,x,a[N],fa[N],cnt[N][2];ll ans;
 6 struct Data{
 7     int k;
 8     bool operator < (const Data &n)const{
 9         ll x=(ll)cnt[k][1]*cnt[n.k][0];
10         ll y=(ll)cnt[k][0]*cnt[n.k][1];
11         return (x!=y ? x<y : k<n.k);
12     }
13 };set<Data>S;
14 int find(int k){
15     return (k==fa[k] ? k : fa[k]=find(fa[k]));
16 }
17 int main(){
18     scanf("%d",&n);
19     for(int i=2;i<=n;i++)scanf("%d",&a[i]);
20     for(int i=1;i<=n;i++)fa[i]=i;
21     for(int i=1;i<=n;i++){
22         scanf("%d",&x),cnt[i][x]=1;
23         if (i>1)S.insert(Data{i});
24     }
25     for(int i=1;i<n;i++){
26         x=(*S.begin()).k,S.erase(S.begin());
27         int f=fa[x]=find(a[x]);
28         if (f!=1)S.erase(Data{f});
29         ans+=(ll)cnt[f][1]*cnt[x][0];
30         cnt[f][0]+=cnt[x][0],cnt[f][1]+=cnt[x][1];
31         if (f!=1)S.insert(Data{f});
32     }
33     printf("%lld\n",ans);
34     return 0;
35 }
View Code

 

posted @ 2021-02-03 08:14  PYWBKTDA  阅读(102)  评论(0编辑  收藏  举报