POJ2054的题意:
有一棵n个节点的树,树上每个节点的权值是a[i],要给这棵树涂色,规定涂某个节点前,它的所有父节点都要被涂色,给每个节点涂色花费一单位时间。时间从1开始,假设涂某个点的时间是t[i],总代价是 ∑ i = 0 n a [ i ] ∗ t [ i ] \sum_{i=0}^n a[i]*t[i] ∑i=0na[i]∗t[i],求最小总代价。( n ≤ 1000 n\le1000 n≤1000)
2018年CCPC桂林A题的题意:
给两个序列A[i],B[i],把它们归并到序列C[i],并要保证在序列C里面A的顺序不变,B的顺序也不变,代价是
∑
i
=
0
n
c
[
i
]
∗
i
\sum_{i=0}^n c[i]*i
∑i=0nc[i]∗i(
n
≤
200000
n\le200000
n≤200000)
那我们把A和B当做两条链并加上根节点就变成POJ2054了。
题解:
如果没有先涂祖先的限制,那么肯定是从大到小涂。有了这个限制后,也应该在涂完父节点后马上涂权值大的节点,这样才能做得和没有限制时更接近。
证明:
父节点是p,大儿子是q,p还有k个小儿子。
先p再q再小儿子们:①
C
p
∗
T
+
C
q
∗
(
T
+
1
)
+
∑
i
=
1
k
C
s
o
n
i
∗
(
T
+
1
+
i
)
Cp*T+Cq*(T+1)+\sum_{i=1}^k Csoni*(T+1+i)
Cp∗T+Cq∗(T+1)+∑i=1kCsoni∗(T+1+i)
先p再小儿子们再q:②
C
p
∗
T
+
∑
i
=
1
k
C
s
o
n
i
∗
(
T
+
i
)
+
C
q
∗
(
T
+
2
)
Cp*T+\sum_{i=1}^k Csoni*(T+i)+Cq*(T+2)
Cp∗T+∑i=1kCsoni∗(T+i)+Cq∗(T+2)
①-②得
∑
i
=
1
k
C
s
o
n
i
∗
i
−
C
q
∗
k
\sum_{i=1}^k Csoni*i-Cq*k
∑i=1kCsoni∗i−Cq∗k
C
q
≥
C
s
o
n
i
,
k
∗
C
q
≥
∑
i
=
1
k
C
s
o
n
i
∗
i
Cq\ge Csoni,k*Cq\ge \sum_{i=1}^k Csoni*i
Cq≥Csoni,k∗Cq≥∑i=1kCsoni∗i
得证
现在我们知道了涂了某个父节点之后要马上涂最大的儿子,可以把它们合并成一个集合,那么有多个集合的时候应该先选哪个呢?
假设集合1:父节点是p,子节点是q
集合2:有k个节点
先1后2:①
C
p
∗
(
T
+
1
)
+
C
q
∗
(
T
+
2
)
+
∑
i
=
1
k
C
i
∗
(
T
+
2
+
i
)
Cp*(T+1)+Cq*(T+2)+\sum_{i=1}^k Ci*(T+2+i)
Cp∗(T+1)+Cq∗(T+2)+∑i=1kCi∗(T+2+i)
先2后1:②
C
p
∗
(
T
+
k
+
1
)
+
C
q
∗
(
T
+
k
+
2
)
+
∑
i
=
1
k
C
i
∗
(
T
+
i
)
Cp*(T+k+1)+Cq*(T+k+2)+\sum_{i=1}^k Ci*(T+i)
Cp∗(T+k+1)+Cq∗(T+k+2)+∑i=1kCi∗(T+i)
①-②得
−
(
C
p
+
C
q
)
∗
k
+
∑
i
=
1
k
C
i
∗
2
-(Cp+Cq)*k+\sum_{i=1}^k Ci*2
−(Cp+Cq)∗k+∑i=1kCi∗2
即
(
C
p
+
C
q
)
/
2
≥
(
∑
i
=
1
k
C
i
)
/
k
(Cp+Cq)/2\ge (\sum_{i=1}^k Ci)/k
(Cp+Cq)/2≥(∑i=1kCi)/k时先1后2
也就是说集合的和的平均值越大的越先。
用优先队列实现带权并查集的合并就可以了。时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<queue>
#define rep(i,l,r) for (int i=l;i<=r;i++)
#define per(i,l,r) for (int i=l;i>=r;i--)
using namespace std;
const int maxn=200500;
struct node{
double avg; int sum,sz,rt,id;
friend bool operator <(node a,node b){
return a.avg<b.avg;
}
}f[maxn];
priority_queue<node> q;
vector<int> g[maxn];
int fa[maxn],a[maxn];
int n,rt,ans,idx;
void dfs(int u){
for (int j=0;j<(int)g[u].size();j++){
int v=g[u][j];
if (v!=fa[u]) fa[v]=u,dfs(v);
}
}
int find(int x){
if (f[x].rt==x) return x;
return f[x].rt=find(f[x].rt);
}
int main() {
while (1){
scanf("%d%d",&n,&rt);
if (!n&&!rt) break;
rep(i,1,n) scanf("%d",&a[i]);
rep(i,1,n) g[i].clear(),fa[i]=0; idx=0; ans=0;
rep(i,1,n) f[i]=(node){1.0*a[i],a[i],1,i,++idx};
rep(i,1,n-1) {
int x,y; scanf("%d%d",&x,&y);
g[x].push_back(y); g[y].push_back(x);
}
dfs(rt);
rep(i,1,n) q.push(f[i]);
while (!q.empty()){
node u=q.top(); q.pop();
if (u.id!=f[u.rt].id) continue;
if (!fa[u.rt]) continue;
ans+=f[find(fa[u.rt])].sz*u.sum;
int x=find(fa[u.rt]);
f[u.rt].rt=x;
f[x].sum+=u.sum,f[x].sz+=u.sz;
f[x].avg=1.0*f[x].sum/f[x].sz,f[x].id=++idx;
q.push(f[x]);
}
rep(i,1,n) ans+=a[i];
printf("%d\n",ans);
}
return 0;
}