[luogu p1955] [NOI2015]程式自動分析
阿新 • • 發佈:2020-08-23
題意:給一棵樹的邊賦邊權,要求邊權積等於m(m以質因數分解形式給出),且邊上1的個數儘量少,目的最大化兩兩點對路徑和之和
呂老闆講過,樹上統計兩兩點對路徑和,考慮每條邊的貢獻,對於一條邊,它貢獻的次數就是它兩側子樹的大小之積,用dfs求出siz,貢獻次數w就是siz*(n-siz)
把w陣列從大到小排序,如何安排m?
m的質因數分解形式也從大到小排序,要求1最少,那就優先給每個w排上一個數,由排序不等式(或者直覺),大的配大的
首先,考慮邊數比m分解的數多的情況,那就儘量安排大的在前面,後面的補1
再考慮邊數比m分解數少的情況,想到兩個情況,把從大到小安排後,把剩下的安排到最大;或者從小到大安排,再安排剩下的到最大
考慮貢獻次數\(w_1\)和\(w_2\)的兩條邊,安排\(a,b,c\)三個數(順序預設從大到小),結果就是\(acw_1+bw_2\)或\(abw_1+cw_2\),顯然第二個大,所以策略是最大的安排到最大的邊,直到剩下的正好覆蓋剩餘的邊
呼之欲出
#include<bits/stdc++.h> using namespace std; inline int rd(){ int ret=0,f=1;char c; while(c=getchar(),!isdigit(c))f=c=='-'?-1:1; while(isdigit(c))ret=ret*10+c-'0',c=getchar(); return ret*f; } #define pc putchar #define space() pc(' ') #define nextline() pc('\n') void pot(int x){if(!x)return;pot(x/10);pc('0'+x%10);} void out(int x){if(!x)pc('0');if(x<0)pc('-'),x=-x;pot(x);} const int MOD = 1e9+7; const int MAXN = 100005; struct Edge{ int next,to; }e[MAXN<<1]; int head[MAXN],ecnt; inline void add(int x,int y){ e[++ecnt].next = head[x]; e[ecnt].to = y; head[x] = ecnt; } typedef long long ll; int n,m; ll ps[MAXN],w[MAXN]; int siz[MAXN]; void dfs(int x,int pre){ siz[x]=1; for(int i=head[x];i;i=e[i].next){ int v=e[i].to; if(v==pre) continue; dfs(v,x); siz[x]+=siz[v]; } } void solve(){ memset(siz,0,sizeof(siz)); memset(head,0,sizeof(head)); memset(ps,0,sizeof(ps)); ecnt=0; //clear n=rd(); int x,y; for(int i=1;i<=n-1;i++){ x=rd();y=rd(); add(x,y);add(y,x); } m=rd(); for(int i=1;i<=m;i++){ ps[i]=rd(); } sort(ps+1,ps+1+m); reverse(ps+1,ps+1+m); dfs(1,-1); for(int i=1;i<=ecnt;i+=2){ int u=e[i].to; int v=e[i+1].to; ll mn=min(siz[u],siz[v]); w[(i+1)>>1]=1ll*mn*(n-mn); // w[(i+1)>>1]=mn; } sort(w+1,w+1+(n-1)); reverse(w+1,w+1+(n-1)); // cerr<<"DEBUG:"; // for(int i=1;i<=n-1;i++) cerr<<w[i]<<" "; // cerr<<endl; ll ans=0; if((n-1)>=m){ for(int i=1;i<=n-1;i++){ int y=ps[i]; if(y==0) y=1; ans+=(ll)(1ll*w[i]*y); ans%=MOD; } // cerr<<"ANS:"; cout<<ans<<endl; return; }else{ ll tmp=1; for(int i=1;i<=(m-n+2);i++){ tmp*=(ll)(1ll*ps[i]); tmp%=MOD; } ans+=(ll)(1ll*tmp*w[1]); ans%=MOD; for(int i=2;i<=n-1;i++){ int j=i+m-n+1; ans+=(ll)(1ll*w[i]*ps[j]); ans%=MOD; } // cerr<<"ANS:"; cout<<ans<<endl; return; } } int main(){ int t=rd(); while(t--) solve(); return 0; }