[HNOI/AHOI2018]排列
阿新 • • 發佈:2019-03-18
() pair fine || gis += read href getc 選了後我們一定會最先選\(i\)
也就是說在最後的排列中\(fa[i]\)和\(i\)是挨在一塊的,但是考慮到實際上多次合並後每個節點就是一個序列
[Luogu4437]
如果\(a[i]=j\)則序列\(p[]\)中\(j\)必須排在\(i\)前面,如果\(j\)不在範圍內則不管,求一個式子\(\sum_{i=1}^n iw_{p[i]}\)的最大值
考慮建出一個圖,連邊\(k=a_j\to j\)方向表示順序,這樣\([1,n]\)每個點的入度都會是\(1\)
如果有環那麽就無解,否則這個圖就是一棵以\(0\)為根樹,如果是在樹上的話,也就是說必須要先選父親才能選兒子
考慮一種貪心
考慮一個當前權值最小的點\(i\)
\(1.\)如果\(i\)沒有父親\((fa[i]=0)\),那麽我們當前一定是選\(i\)
\(2.\)如果\(i\)有父親,那麽當\(fa[i]\)
也就是說在最後的排列中\(fa[i]\)和\(i\)是挨在一塊的,但是考慮到實際上多次合並後每個節點就是一個序列
手模以後發現,平均權值小的放前面答案會更優
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<queue> #include<cassert> #define debug(...) fprintf(stderr,__VA_ARGS__) #define Debug(x) cout<<#x<<"="<<x<<endl using namespace std; typedef long long LL; typedef long double ld; const int INF=1e9+7; typedef pair<ld,int> pdi; inline LL read(){ register LL x=0,f=1;register char c=getchar(); while(c<48||c>57){if(c=='-')f=-1;c=getchar();} while(c>=48&&c<=57)x=(x<<3)+(x<<1)+(c&15),c=getchar(); return f*x; } const int MAXN=5e5+5; LL w[MAXN],done[MAXN]; int Fa[MAXN],a[MAXN]; int n;LL ans; priority_queue <pdi,vector<pdi>,greater<pdi> > q,t; inline int getfa(int x){return Fa[x]==x?x:Fa[x]=getfa(Fa[x]);} int main(){ n=read(); for(int i=1;i<=n;i++) Fa[i]=i; for(int i=1;i<=n;i++){ a[i]=read(); int fx=getfa(i),fy=getfa(a[i]); if(fx==fy){printf("-1\n");return 0;} Fa[fx]=fy;//直接用並查集判環 } for(int i=1;i<=n;i++) ans+=(w[i]=read());//先加一遍 for(int i=0;i<=n;i++) Fa[i]=i; for(int i=1;i<=n;i++){ done[i]=1; q.push(pdi((ld)w[i],i)); } for(int i=1;i<=n;i++){ while(!t.empty()&&q.top()==t.top()){//每次刪掉q.top(),可以保證正確性 t.pop();q.pop(); } int x=q.top().second;q.pop(); assert(x==Fa[x]); int y=getfa(a[x]); if(y) t.push(pdi((ld)w[y]*1.0/done[y],y));//刪掉 ans+=w[x]*done[y];//之前已經選了多少個,現在就接著選 w[y]+=w[x],done[y]+=done[x],Fa[x]=y;//合並個數到上一層(y可以等於0) if(y) q.push(pdi((ld)w[y]*1.0/done[y],y));//更新 } printf("%lld\n",ans); }
[HNOI/AHOI2018]排列