膜了一下PIPIBOSS的题解
我觉得我自己描述的都没有他好
所以就贴吧:
首先注意到实际上约束关系构成了一棵树
考虑这个排列 pp,编号为 a[i]a[i] 的出现了,ii 才可以出现
那么如果连边 (a[i],i)(a[i],i),就会构成一棵以 00 为根的树,每一个点只有一个父亲
否则就不合法
因为要父亲被选入,这个点才能被选入,所以排列 pp,相当于是这棵树的一种合法的拓扑序
要求的就是代价最大的一个拓扑序
那么问题就和 POJ2054POJ2054 一样的做法了,用一个神奇的贪心
每次找出全局的权值最小值,往父亲合并,合并成新节点,权值为平均值,即 ∑wisize∑wisize
答案加上被合并的点的权值乘以父亲的 sizesize
正确性感性理解一下,具体证明和国王游戏差不多,发现 swapswap 之后不会更优
果然set被卡了
Code:
#pragma GCC opztime(2)
#include<bits/stdc++.h>
#define ri register
#define ll long long
using namespace std;
inline int read(){
int res=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-f;ch=getchar();}
while(isdigit(ch)) {res=(res<<1)+(res<<3)+(ch^48);ch=getchar();}
return res*f;
}
const int N=1e6+5;
struct node{ll v;int s,x;}p[N];
int vis[N<<1],head[N<<1],nxt[N<<1],tot=0;
int n,a[N],ff[N],cnt=0,w[N],in[N],fa[N];
inline void add(int x,int y){vis[++tot]=y;nxt[tot]=head[x];head[x]=tot;}
inline bool topsort(){
queue<int>q;
q.push(0);
while(!q.empty()){
int x=q.front();q.pop();
for(int i=head[x];i;i=nxt[i]) if(!--in[vis[i]]) q.push(vis[i]);
}
for(ri int i=1;i<=n;i++) if(in[i]>0) return 0;
return 1;
}
inline bool operator < (const node &a,const node &b){if(a.v*b.s==b.v*a.s) return a.x<b.x;return a.v*b.s<b.v*a.s;}
set<node>s;
inline int get(int v){return v==ff[v]?v:ff[v]=get(ff[v]);}
int main(){
n=read();
for(ri int i=1;i<=n;i++) a[i]=read(),add(a[i],i),in[i]++,fa[i]=a[i];
for(ri int i=1;i<=n;i++) w[i]=read();
if(!topsort()) {puts("-1");return 0;}
for(ri int i=1;i<=n;i++){
p[i].v=w[i],p[i].s=1,p[i].x=i;
s.insert(p[i]);ff[i]=i;
}
cnt=n;p[0].s=1;
ll ans=0;node t;
while(!s.empty()){
t=*s.begin();s.erase(t);
int y=get(fa[t.x]);
ans+=t.v*p[y].s;
if(y){
s.erase(p[y]);node e=t;
e.v+=p[y].v;e.s+=p[y].s;e.x=++cnt;
ff[cnt]=cnt;ff[y]=cnt;ff[get(t.x)]=cnt;
fa[cnt]=fa[y];fa[t.x]=cnt;
p[cnt]=e;s.insert(e);
}
else ff[get(t.x)]=0,p[0].s+=t.s;
}
cout<<ans<<endl;
return 0;
}
本文详细解析了BZOJ5289题目的解题思路,通过构建一棵树来表达题目中的约束关系,并使用拓扑排序和贪心算法求解最大代价的排列。代码实现中运用了C++的数据结构和算法,如队列、集合和自定义比较函数。

125

被折叠的 条评论
为什么被折叠?



