zoukankan      html  css  js  c++  java
  • 洛谷 P4437

    题解咕到了现在。。

    洛谷题目页面传送门

    (n) 个数 (b_i),你需要重新排列 (b) 得到 (b') 使得 (b_{a_i}) 必须排在 (b_i) 前面。最大化 (sumlimits_{i=1}^nib'_i) 或报告无解。

    (ninleft[1,5 imes10^5 ight])

    首先建图。如果有环的话肯定就无解,否则不难发现一定是一个森林。于是就转化成了 POJ 2054(只不过 POJ 的这题平方可过)。

    首先想到的是贪心,每次选当前能选的节点集合中最小的,而这肯定是不行的,反例随便举。

    考虑当前可选的节点集合中任意两个 (x,y),来比较当前选它们两个哪个更好。一个比较自然的想法是尝试找到一种方法,能将所有选 (x) 的方案交换几次变成选 (y) 并且更优。那我们就来看,我们假设当前选的是 (x),然后 (y) 在位置 (p)。考虑将 (y) 移动到 (x) 当前位置,然后将 (x) 换过去?可能会不行,因为 ([1,p]) 内可能会有依赖于 (x) 的。

    想到一种方法,即将 (y) 移到最前面,然后将 ([1,p]) 内依赖于或等于 (x) 的子序列整体顺移。但是这样又不太好比较是否更优了,因为涉及到了 (x) 的无穷匮子孙。那我们只好退一万步,假设 (y) 是全树权值最小的节点,那么不难证明换过去一定更优(证明就是你会发现 (x) 的子孙们移动的距离之和等于 (p-1),恰好每格与 (y) 对应,那么每次 (y) 肯定能干得过其他节点,因为他是最强的嘛)。

    你现在虽然能保证一定更优了,但是你毕竟退步了,是有条件的,即必须是全树最小点。也就意味着不是任何时候我们都能应用这个理论来比较优劣。那怎么办呢?我们不考虑当前,我们规划未来,即一旦全树最小点进入了候选集合,那么最先选的一定是它。那么什么时候它进入候选集合呢?那显然是当且仅当它爸爸被选了的时候,也就转化成了最小点爸爸被选之后一定选它。那么不难想到将它和它爸爸合并,这样总结点数 (-1),似乎可以归纳了。

    然后你又会发现问题,因为想要归纳的话,你在任意时刻面临的节点不再是单一的节点,而可能是若干个曾经的节点合并起来的合体。那么我们需要将先前那条理论上升一下。直觉告诉我们应该是内部包含数们的平均数最小的合体。推理起来也不难,我们设 ([1,p]) 内对应位置上的合体大小为 (sz_i),和为 (sum_i),平均数为 (avg_i),移动前后位置差为 (d_i),那么显然有 (sumlimits_{i=1}^p sz_id_i=0)。而每个合体对答案变化量的贡献是 (sum_id_i),也即 (avg_isz_id_i),又因为 (sz_id_i) 是守恒的,所以选 (avg) 最小的一定没问题。

    实现就比较简单了,用 set 随便维护一下即可。

    根据考察能力守恒定律,思维题代码一定很好写(

    #include<bits/stdc++.h>
    using namespace std;
    #define int long long
    #define mp make_pair
    #define Y second
    const int N=500000;
    void read(int &x){
    	x=0;char c=getchar();
    	while(!isdigit(c))c=getchar();
    	while(isdigit(c))x=(x<<1)+(x<<3)+(c^48),c=getchar();
    }
    int n;
    int a[N+1],b[N+1];
    int vis[N+1];
    void dfs(int x){
    	if(vis[x]==2)exit((puts("-1"),0));
    	if(vis[x]==1)return;
    	vis[x]=2;
    	if(a[x])dfs(a[x]);
    	vis[x]=1;
    }
    struct ufset{
    	int ycx[N+2],sz[N+2];
    	void init(){for(int i=1;i<=n+1;i++)ycx[i]=0,sz[i]=i>1;}
    	int root(int x){return ycx[x]?ycx[x]=root(ycx[x]):x;}
    	void mrg(int x,int y){x=root(x),y=root(y),sz[y]+=sz[x],ycx[x]=y;}
    }ufs;
    int ans[N+1],sum[N+1];
    struct frac{
    	int a,b;
    	frac(int x,int y){a=x,b=y;}
    	friend bool operator<(frac x,frac y){
    		return x.a*y.b<y.a*x.b;
    	}
    };
    signed main(){
    	cin>>n;
    	for(int i=1;i<=n;i++)read(a[i]);
    	for(int i=1;i<=n;i++)read(b[i]);
    	for(int i=1;i<=n;i++)if(!vis[i])dfs(i);
    	set<pair<frac,int> > st;
    	for(int i=1;i<=n;i++)st.insert(mp(frac(b[i],1),i)),ans[i]=sum[i]=b[i];
    	ufs.init();
    	while(st.size()){
    		int x=st.begin()->Y;
    		int hsc=ufs.root(x+1)-1,ycx=ufs.root(a[hsc]+1)-1;
    //		cout<<hsc<<" "<<ycx<<" "<<sum[hsc]<<" "<<sum[ycx]<<"!
    ";
    		st.erase(st.begin());
    		if(ycx)st.erase(st.find(mp(frac(sum[ycx],ufs.sz[ycx+1]),ycx)));
    		ans[ycx]+=ans[hsc]+sum[hsc]*ufs.sz[ycx+1];
    		sum[ycx]+=sum[hsc];
    		ufs.mrg(hsc+1,ycx+1);//确定关系 
    		if(ycx)st.insert(mp(frac(sum[ycx],ufs.sz[ycx+1]),ycx));
    	}
    	cout<<ans[0];
    	return 0;
    }
    

    顺便插一嘴,某 libra 姓神仙似乎提出了一个对平方 DP 的平衡树 + 启发式合并的平方二次对数的优化,不过似乎是假的,欢迎去 D 她(

  • 相关阅读:
    Office Shared-Addin : Favorite的下载、安装和使用(2020.2.22)
    VBA编程常用词汇英汉对照表
    Excel-DNA自定义函数的参数智能提示功能:ExcelDna.IntelliSense1.1.0.rar
    VSTO开发中级教程 配套资源下载
    TreeviewEditor.rar
    FaceIDViewer.rar
    imageMso7345.rar
    VisualStudioAddin2016Setup.rar
    VBE2014_Setup_20160709.rar
    documen.write 和 innerHTML 的区别?
  • 原文地址:https://www.cnblogs.com/ycx-akioi/p/Luogu-P4437.html
Copyright © 2011-2022 走看看