[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)$,可以通过
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }