BZOJ.5289.[AHOI/HNOI2018]排列(贪心 heap)

BZOJ
LOJ
洛谷


\(Kelin\)写的挺清楚的...
要求如果\(a_{p_j}=p_k\)\(k\lt j\),可以理解为\(k\)要在\(j\)之前选。
那么对于给定的\(a_j=k\),我们可以连边\(k\to j\)建一张图。如果图有环,那么无解;否则这是一棵以\(0\)为根的树。
限制就变成了,选一个点前必须要选其父亲;如果第\(k\)个选点\(i\)\(i\)的贡献是\(k\cdot w_i\)

然后怎么做呢...
假设当前最小的数是\(x\),那么如果\(fa[x]\)选过,现在一定选\(x\);否则如果有一次选了\(fa[x]\),下一次一定选\(x\)
考虑到按顺序选点实际是构成一个序列,且上面的\(x\)一定和\(fa[x]\)在序列中挨在一起。所以考虑把\(x,fa[x]\)合并成一个序列,再考虑其它的。
之前是比点权,现在我们要比较序列与序列之间谁放在后面更优。
考虑两个序列\(a,b\),设\(a\)中有\(t_1\)个点,\(b\)中有\(t_2\)个点,假设现在已经选了\(k\)个点,那么\(ab,ba\)的贡献分别为\[W_{ab}=\sum_{i=1}^{t_1}(k+i)w_{a_i}+\sum_{i=1}^{t_2}(k+i+t_1)w_{b_i}\\W_{ba}=\sum_{i=1}^{t_2}(k+i)w_{b_i}+\sum_{i=1}^{t_1}(k+i+t_2)w_{a_i}\\W_{ab}-W_{ba}=t_1\sum w_{b_i}-t_2\sum w_{a_i}\]

那么\(W_{ab}>W_{ba}\Rightarrow \frac{\sum w_{b_i}}{t_2}>\frac{\sum w_{a_i}}{t_1}\),即平均数更小的放到前面更优。
所以我们就可以拿堆维护了,每次取出最小的,把它与父节点所在序列合并。这里可以用带修改堆修改父节点的状态,也可以打个标记删掉父节点的状态(其实判一下\(sz\)是否等于当前状态下的\(sz\)就可以了)。
答案就在一个序列被放到后面的时候统计就可以了。

另外堆也可以用\(STL\)里的\(heap\)。只需要#include <algorithm>
常数应该是比priority_queue小一些的?可能是我写的丑...


priority_queue

//30268kb   2620ms
#include <queue>
#include <cstdio>
#include <cctype>
#include <cstdlib>
#include <algorithm>
//#define gc() getchar()
#define MAXIN 500000
#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
typedef long long LL;
const int N=5e5+5;

int Cnt,fa[N],Enum,H[N],nxt[N],to[N],F[N],sz[N];
LL w[N];
char IN[MAXIN],*SS=IN,*TT=IN;
struct Node
{
    int x,sz; LL val;
    bool operator <(const Node &x)const
    {
        return val*x.sz>x.val*sz;
    }
};

inline int read()
{
    int now=0;register char c=gc();
    for(;!isdigit(c);c=gc());
    for(;isdigit(c);now=now*10+c-48,c=gc());
    return now;
}
inline void AE(int u,int v)
{
    to[++Enum]=v, nxt[Enum]=H[u], H[u]=Enum;
}
void DFS(int x)
{
    static bool vis[N];
    ++Cnt, vis[x]=1;
    for(int i=H[x]; i; i=nxt[i])
        if(!vis[to[i]]) DFS(to[i]);
        else {puts("-1"); exit(0);}
}
int Find(int x)
{
    return x==F[x]?x:F[x]=Find(F[x]);
}

int main()
{
//  freopen("perm.in","r",stdin);
//  freopen("perm.out","w",stdout);

    const int n=read();
    for(int i=1; i<=n; ++i) AE(fa[i]=read(),i);
    DFS(0);
    if(Cnt<=n) return puts("-1"),0;
    std::priority_queue<Node> q;
    for(int i=0; i<=n; ++i) F[i]=i, sz[i]=1;//from 0
    for(int i=1; i<=n; ++i) q.push((Node){i,1,w[i]=read()});
    LL ans=0;
    while(!q.empty())
    {
        Node tmp=q.top(); q.pop();
        int x=Find(tmp.x);
        if(tmp.sz!=sz[x]) continue;
        int r=Find(fa[x]);
        F[x]=r, ans+=tmp.val*sz[r], sz[r]+=sz[x], w[r]+=w[x];
        if(r) q.push((Node){r,sz[r],w[r]});
    }
    printf("%lld\n",ans);

    return 0;
}

heap

//25792kb   2716ms
#include <cstdio>
#include <cctype>
#include <cstdlib>
#include <algorithm>
//#define gc() getchar()
#define MAXIN 500000
#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
typedef long long LL;
const int N=5e5+5;

int Cnt,fa[N],Enum,H[N],nxt[N],to[N],F[N],sz[N];
LL w[N];
char IN[MAXIN],*SS=IN,*TT=IN;
struct Node
{
    int x,sz; LL val;
    bool operator <(const Node &x)const
    {
        return val*x.sz>x.val*sz;
    }
}q[N];

inline int read()
{
    int now=0;register char c=gc();
    for(;!isdigit(c);c=gc());
    for(;isdigit(c);now=now*10+c-48,c=gc());
    return now;
}
inline void AE(int u,int v)
{
    to[++Enum]=v, nxt[Enum]=H[u], H[u]=Enum;
}
void DFS(int x)
{
    static bool vis[N];
    ++Cnt, vis[x]=1;
    for(int i=H[x]; i; i=nxt[i])
        if(!vis[to[i]]) DFS(to[i]);
        else {puts("-1"); exit(0);}
}
int Find(int x)
{
    return x==F[x]?x:F[x]=Find(F[x]);
}

int main()
{
//  freopen("perm.in","r",stdin);
//  freopen("perm.out","w",stdout);

    const int n=read();
    for(int i=1; i<=n; ++i) AE(fa[i]=read(),i);
    DFS(0);
    if(Cnt<=n) return puts("-1"),0;
    for(int i=0; i<=n; ++i) F[i]=i, sz[i]=1;//from 0
    for(int i=1; i<=n; ++i) q[i]=(Node){i,1,w[i]=read()};
    LL ans=0; int cnt=n;
    std::make_heap(q+1,q+1+cnt);
    while(cnt)
    {
        Node tmp=q[1]; std::pop_heap(q+1,q+1+cnt), --cnt;
        int x=Find(tmp.x);
        if(tmp.sz!=sz[x]) continue;
        int r=Find(fa[x]);
        F[x]=r, ans+=tmp.val*sz[r], sz[r]+=sz[x], w[r]+=w[x];
        if(r) q[++cnt]=(Node){r,sz[r],w[r]}, std::push_heap(q+1,q+1+cnt);
    }
    printf("%lld\n",ans);

    return 0;
}

转载于:https://www.cnblogs.com/SovietPower/p/10438041.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值