Bzoj5289:[Hnoi2018]排列

[HNOI/AHOI2018]排列

题目看这里

题解

%20 搜索枚举全排列
%40 给好的搜索??随机化一下吧,人品可以的应该能拿到吧。
%60 距离正解差了一个log?发现是树然后暴力枚举???
%80 都有这个分数了,为什么不拿100???

正解:
很显然的是对于\(a[i]\),我们直接把\(i\)\(a[i]\)连一条边
没有0时,无解,听说还要判环
n+1个点,n条边,连同==>树。
然后原题变成:
给出一棵以 0 为根的有根树,需要为非 0 顶点标号 1∼n ,并且满足父亲比自己先标号。每个节点有点权,树的价值为点权乘标号的和。求树最大的价值。
一个很显然的贪心就是如果当前节点权值很小,且它的父亲被选取了,那么优先给它标号。

NaVi_Awson爷这里给出了很好的解释,以下原话照搬

考虑如果 \(u\) 有父亲,显然当他的父亲被选之后马上就会选 \(u\) ,也就是说父子间的编号一定是相邻的。我们可以将 \(u\) 的答案并在他的父亲中。

同样的,对于两个不同的“块”,也是如此。

考虑一个长度为 \(l1\) 的序列 \(A\) 和一个长度为 \(l_2\) 的序列 \(B\)

序列前面已经安排好了 \(loc\) 个。考虑 \(AB\)\(BA\) 两种合并后的序列的答案:

\(W_{AB}=\sum_{i=1}^{l_1}(i+loc)w_{A_i}+\sum_{i=1}^{l_2}(i+loc+l_1)w_{B_i}\)

\(W_{BA}=\sum_{i=1}^{l_2}(i+loc)w_{B_i}+\sum_{i=1}^{l_1}(i+loc+l_2)w_{A_i}\)

如果 \(W_{AB}> W_{BA}\Rightarrow \frac{\sum_{i=1}^{l_1}w_{A_i}}{l_1}<\frac{\sum_{i=1}^{l_2}w_{B_i}}{l_2}\)

也就是平均权值小的放前面答案会更优。

Code

但是不吸氧要TLEqwq。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
#include<cmath>
#define ll long long 
using namespace std;
const ll N=5e5+5;
ll n,a[N],fa[N],sz[N];
ll w[N],flag=1;
struct node{
    ll id,vi,sz;
    node(ll _id=0,ll _vi=0,ll _sz=0){id=_id,vi=_vi,sz=_sz;}
    bool operator < (const node &b) const {return vi*b.sz>b.vi*sz;}
};
priority_queue<node>q;
ll read(){
    ll x=0,w=1;char ch=getchar();
    while(ch>'9'||ch<'0'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*w;
}
ll find(ll x){return x==fa[x]?x:fa[x]=find(fa[x]);}
int main(){
    n=read();
    for(ll i=1;i<=n;i++){
        a[i]=read();if(a[i]==0)flag=0;
    }if(flag){cout<<"-1"<<endl;return 0;}
    ll ans=0;ll len=0;
    for(ll i=1;i<=n;i++){
        w[i]=read();
        q.push(node(i,w[i],1));
        sz[i]=1;ans+=w[i];
    }
    for(ll i=0;i<=n;i++)fa[i]=i;
    while(!q.empty()){
        node t=q.top();q.pop();
        if(sz[t.id]!=t.sz)continue;
        if(find(a[t.id])==0){
            ans+=len*w[t.id];fa[t.id]=0;len+=t.sz;
        }
        else {
            ll tmp=find(a[t.id]);
            ans+=w[t.id]*sz[tmp];fa[t.id]=tmp;
            w[tmp]+=w[t.id];sz[tmp]+=sz[t.id];
            q.push(node(tmp,w[tmp],sz[tmp]));
        }
    }cout<<ans<<endl;
    return 0;
}

转载于:https://www.cnblogs.com/hhh1109/p/10388351.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值