• bzoj 5289: [Hnoi2018]排列


    Description

    Solution

    首先注意到实际上约束关系构成了一棵树
    考虑这个排列 (p),编号为 (a[i]) 的出现了,(i) 才可以出现
    那么如果连边 ((a[i],i)),就会构成一棵以 (0) 为根的树,每一个点只有一个父亲
    否则就不合法

    因为要父亲被选入,这个点才能被选入,所以排列 (p),相当于是这棵树的一种合法的拓扑序
    要求的就是代价最大的一个拓扑序
    那么问题就和 (POJ\,2054) 一样的做法了,用一个神奇的贪心

    每次找出全局的权值最小值,往父亲合并,合并成新节点,权值为平均值,即 (frac{sum w_i}{size})
    答案加上被合并的点的权值乘以父亲的 (size)
    正确性感性理解一下,具体证明和国王游戏差不多,发现 (swap) 之后不会更优
    实现可以用一个堆或者 (set) 实现
    然而 (set) 被卡常了,开 (O2) 才能过

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N=1000010;
    int n,a[N],w[N],head[N],nxt[N],to[N],num=0,in[N],fa[N];
    inline void link(int x,int y){nxt[++num]=head[x];to[num]=y;head[x]=num;}
    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]){
    			int u=to[i];
    			if(!(--in[u]))Q.push(u);
    		}
    	}
    	for(int i=1;i<=n;i++)if(in[i]>0)return false;
    	return true;
    }
    struct data{
    	ll w;int s,x;
    	bool operator <(const data &p)const{
    		if(w*p.s!=p.w*s)return w*p.s<p.w*s;
    		return x<p.x;
    	}
    }p[N];
    set<data>Q;
    int cnt=0,b[N];
    inline int find(int x){return b[x]==x?x:b[x]=find(b[x]);}
    int main(){
      freopen("perm.in","r",stdin);
      freopen("perm.out","w",stdout);
      scanf("%d",&n);
      for(int i=1;i<=n;i++)
            scanf("%d",&a[i]),link(a[i],i),in[i]++,fa[i]=a[i];
      for(int i=1;i<=n;i++)scanf("%d",&w[i]);
      if(!topsort()){puts("-1");return 0;}
      for(int i=1;i<=n;i++){
    	  p[i]=(data){w[i],1,i};
    	  Q.insert(p[i]);b[i]=i;
      }
      cnt=n;p[0].s=1;
      ll ans=0;
      data t;
      while(!Q.empty()){
    	  t=*Q.begin();Q.erase(t);
    	  int y=find(fa[t.x]);
    	  ans+=t.w*p[y].s;
    	  if(y){
    		  Q.erase(p[y]);
    		  data e=t;
    		  e.w+=p[y].w;e.s+=p[y].s;e.x=++cnt;
    		  b[cnt]=cnt;b[y]=cnt;b[find(t.x)]=cnt;
    		  fa[cnt]=fa[y];fa[t.x]=cnt;
    		  p[cnt]=e;
    		  Q.insert(e);
    	  }
    	  else b[find(t.x)]=0,p[0].s+=t.s;
      }
      cout<<ans<<endl;
      return 0;
    }
    
    
  • 相关阅读:
    luogu P1382 楼房
    luogu P1908 逆序对
    5.28 模拟赛
    POJ 2991 Crane
    残(矩阵快速幂)
    AC日记——拍照 洛谷 P3410
    AC日记——[CQOI2014]危桥 洛谷 P3163
    AC日记——【模板】二分图匹配 洛谷 P3386
    AC日记——[ZJOI2009]假期的宿舍 cogs 1333
    AC日记——城市 洛谷 P1401
  • 原文地址:https://www.cnblogs.com/Yuzao/p/8871713.html
Copyright © 2020-2023  润新知