! AHOI/HNOI2018排列
作者:互联网
第\(i\)个数要放在第\(a_i\)个数的后面
有环无解
原思路:
连边,拓扑排序,只是每次弹出时弹\(w_i\)最大的
但这是错误的贪心思路,因为可能较小的后面都是很大的数
SOL:
还是连边,发现形成了一棵以0为根的树
当前权值最小的点\(i\)
- 若无父亲\(a_i=0\),则直接选
- 若有父亲,一定是选了父亲立马选这个点,于是合并\(i\)与父亲
多次合并后每个点为一个序列,那么怎么判谁最小呢/
\(a,b\)序列的先后关系
\(W_{ab}=\sum_{i=1}^{sz_a}w_{a_i}+\sum_{i=1}^{sz_b}(i+sz_a)w_{b_i}\)
\(W_{ba}=\sum_{i=1}^{sz_b}w_{b_i}+\sum_{i=1}^{sz_a}(i+sz_b)w_{a_i}\)
\(W_{ab}-W_{ba}=sz_aW_b-sz_bW_a\)
\(W_{ab}>W_{ba}\to\frac{W_a}{sz_a}<\frac{W_b}{sz_b}\)
平均值小的放前面会更优
用\(set\)找最小点,合并用并查集,并算一下贡献
时间复杂度\(O(nlog_n)\)
#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int read(){
int x=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
return f==1?x:-x;
}
const int N=5e5+4;
int n,fa[N],a[N],w[N],sz[N],ans;
int find(int x){
return fa[x]==x?x:fa[x]=find(fa[x]);
}
struct cmp{
bool operator()(int x,int y){
return sz[x]*w[y]==w[x]*sz[y]?x<y:sz[x]*w[y]>w[x]*sz[y];
}//平均小的放前面更优
};
set<int,cmp>s;
signed main(){
n=read();
for(int i=1;i<=n;i++)a[i]=read();
for(int i=1;i<=n;i++){
w[i]=read();
sz[i]=1;
s.insert(i);
fa[i]=i;
}
sz[0]=1;//!以0为根的树
for(int i=1;i<=n;i++){
int x=*s.begin(),fx=find(a[find(x)]);
if(find(x)==fx){puts("-1");return (0-0);}
s.erase(s.begin());
if(fx)s.erase(fx);
ans+=sz[fx]*w[x];
sz[fx]+=sz[x];
w[fx]+=w[x];
fa[x]=fx;
if(fx)s.insert(fx);
}
cout<<ans;
return (0-0);
}
标签:sz,排列,ba,AHOI,int,sum,ab,HNOI2018,getchar 来源: https://www.cnblogs.com/aurora2004/p/12582425.html