[BZOJ5289][HNOI2018]排列(拓扑排序+pb_ds)
首先确定将所有a[i]向i连边之后会形成一张图,图上每条有向边i->j表示i要在j之前选。
图上的每个拓扑序都对应一种方案(如果有环显然无解),经过一系列推导可以发现贪心策略与合并的块的大小和w之和有关,具体见https://kelin.blog.luogu.org/solution-p4437
贪心的时候每次要选w平均值最大的,这个可以用STL维护,具体使用哪种见下。
一:STL-priority_queue
最简单直接的做法,每次更新的时候直接加入即可,后面弹出的时候判一下这个点是否已经被更新即可。
BZOJ上AC,Luogu上开O2能A,不开会RE两个点。
1 #include<cstdio> 2 #include<queue> 3 #include<algorithm> 4 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 5 typedef long long ll; 6 using namespace std; 7 8 const int N=500100; 9 int n,u,p,fa[N],tim,cnt,vis[N],f[N],sz[N],h[N],nxt[N<<1],to[N<<1]; 10 ll ans,w[N]; 11 struct D{ int u,sz; ll w; bool operator <(const D &b)const{ return w*b.sz>b.w*sz; } }; 12 priority_queue<D>Q; 13 14 void add(int u,int v){ to[++cnt]=v; nxt[cnt]=h[u]; h[u]=cnt; } 15 16 void dfs(int u){ 17 vis[u]=1; tim++; 18 for (int i=h[u],v; i; i=nxt[i]) 19 if (vis[v=to[i]]) { puts("-1"); exit(0); } else dfs(v); 20 } 21 22 int get(int x){ return (f[x]==x) ? x : f[x]=get(f[x]); } 23 24 int main(){ 25 freopen("bzoj5289.in","r",stdin); 26 freopen("bzoj5289.out","w",stdout); 27 scanf("%d",&n); 28 rep(i,1,n) scanf("%d",&fa[i]),add(fa[i],i); 29 dfs(0); if (tim<=n) { puts("-1"); return 0; } 30 rep(i,0,n) f[i]=i,sz[i]=1; 31 rep(i,1,n) scanf("%lld",&w[i]),Q.push((D){i,1,w[i]}); 32 while (!Q.empty()){ 33 D s=Q.top(); Q.pop(); 34 if (sz[u=get(s.u)]!=s.sz) continue; 35 f[u]=p=get(fa[u]); ans+=w[u]*sz[p]; 36 w[p]+=w[u]; sz[p]+=sz[u]; 37 if (p) Q.push((D){p,sz[p],w[p]}); 38 } 39 printf("%lld\n",ans); 40 return 0; 41 }
二:STL-set
我也不知道上面的方法为什么会RE,然后换成set就不存在这个问题了,取而代之的是超大常数。。
BZOJ上AC,Luogu上开O2能A,不开会TLE两个点。
1 #include<cstdio> 2 #include<set> 3 #include<algorithm> 4 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 5 typedef long long ll; 6 using namespace std; 7 8 const int N=500100; 9 int n,u,p,fa[N],tim,cnt,vis[N],f[N],sz[N],h[N],nxt[N<<1],to[N<<1]; 10 ll ans,w[N]; 11 struct D{ 12 int u,sz; ll w; 13 bool operator <(const D &b)const{ return (w*b.sz!=b.w*sz) ? w*b.sz<b.w*sz : ((u!=b.u)?u<b.u:(sz<b.sz)); } 14 }; 15 multiset<D>Q; 16 17 void add(int u,int v){ to[++cnt]=v; nxt[cnt]=h[u]; h[u]=cnt; } 18 19 void dfs(int u){ 20 vis[u]=1; tim++; 21 for (int i=h[u],v; i; i=nxt[i]) 22 if (vis[v=to[i]]) { puts("-1"); exit(0); } else dfs(v); 23 } 24 25 int get(int x){ return (f[x]==x) ? x : f[x]=get(f[x]); } 26 27 int main(){ 28 freopen("bzoj5289.in","r",stdin); 29 freopen("bzoj5289.out","w",stdout); 30 scanf("%d",&n); 31 rep(i,1,n) scanf("%d",&fa[i]),add(fa[i],i); 32 dfs(0); if (tim<=n) { puts("-1"); return 0; } 33 rep(i,0,n) f[i]=i,sz[i]=1; 34 rep(i,1,n) scanf("%lld",&w[i]),Q.insert((D){i,1,w[i]}); 35 while (!Q.empty()){ 36 D s=*Q.begin(); Q.erase(s); 37 if (sz[u=get(s.u)]!=s.sz) continue; 38 f[u]=p=get(fa[u]); ans+=w[u]*sz[p]; 39 w[p]+=w[u]; sz[p]+=sz[u]; 40 if (p) Q.insert((D){p,sz[p],w[p]}); 41 } 42 printf("%lld\n",ans); 43 return 0; 44 }
三:__gnu_pbds::priority_queue
pb_ds库里的堆天生支持修改,但一般常数将是STL的接近三倍。
但是这题并没有体现,视平台不同而有所差异,BZOJ上和set同速,本机甚至比set和STL-priority_queue都快。
BZOJ上AC,Luogu上AC。
1 #include<cstdio> 2 #include<ext/pb_ds/assoc_container.hpp> 3 #include<ext/pb_ds/priority_queue.hpp> 4 #include<algorithm> 5 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 6 typedef long long ll; 7 using namespace std; 8 9 const int N=500100; 10 int n,u,p,fa[N],tim,cnt,vis[N],f[N],sz[N],h[N],nxt[N<<1],to[N<<1]; 11 ll ans,w[N]; 12 struct D{ int u; ll w; }; 13 struct Cmp{ 14 bool operator()(const D &a,const D &b)const 15 { return (a.w*sz[b.u]!=b.w*sz[a.u]) ? a.w*sz[b.u]>b.w*sz[a.u] : ((a.u!=b.u)?a.u>b.u:(sz[a.u]>sz[b.u])); } 16 }; 17 __gnu_pbds::priority_queue<D,Cmp>Q; 18 __gnu_pbds::priority_queue<D,Cmp>::point_iterator its[N]; 19 20 void add(int u,int v){ to[++cnt]=v; nxt[cnt]=h[u]; h[u]=cnt; } 21 22 void dfs(int u){ 23 vis[u]=1; tim++; 24 for (int i=h[u],v; i; i=nxt[i]) 25 if (vis[v=to[i]]) { puts("-1"); exit(0); } else dfs(v); 26 } 27 28 int get(int x){ return (f[x]==x) ? x : f[x]=get(f[x]); } 29 30 int main(){ 31 freopen("bzoj5289.in","r",stdin); 32 freopen("bzoj5289.out","w",stdout); 33 scanf("%d",&n); 34 rep(i,1,n) scanf("%d",&fa[i]),add(fa[i],i); 35 dfs(0); if (tim<=n) { puts("-1"); return 0; } 36 rep(i,0,n) f[i]=i,sz[i]=1; 37 rep(i,1,n) scanf("%lld",&w[i]),its[i]=Q.push((D){i,w[i]}); 38 while (!Q.empty()){ 39 int u=Q.top().u; Q.pop(); 40 f[u]=p=get(fa[u]); ans+=w[u]*sz[p]; 41 w[p]+=w[u]; sz[p]+=sz[u]; 42 if (p) Q.modify(its[p],(D){p,w[p]}); 43 } 44 printf("%lld\n",ans); 45 return 0; 46 }