HDU 6133 Army Formations
dsu on tree
題意
給你一棵n個節點的二叉樹,每個節點有一個提交任務的時間,每個節點總的提交任務的罰時爲:提交這個節點和其子樹所有的任務,每個任務提交時間的總和爲該點的罰時。求每個節點提交完所有任務的最小罰時
思路
樹上啓發式合併。
考慮有這樣一個數據結構, 可以動態往一個多重集裏面添加數字, 刪除數字, 並查詢多重集中元素的sumofsum.
這個非常簡單, 用樹狀數組就好了.
然後我們考慮這樣一個啓發式過程. 定義一個dfs(root)
, root表示當前子樹的根, 用left_son, right_son表示兩個兒子, 且左子樹的大小比右子樹小.
def tree_remove(root):
for x in tree(root):
remove_from_multiset(x)
def tree_add(root):
for x in tree(root):
add_into_multiset(x)
def dfs(root):
dfs(left_son)
tree_remove(left_son)
dfs(right_son)
tree_add(left_son)
add_into_multiset(root)
f[root] = ask_sumofsum()
其中 add_into_multiset
, remove_from_multiset
, ask_sumofsum
爲上面說到的數據結構支持的操作: 添加數字, 刪除數字, 查詢sumofsum.
而tree(root)表示以root爲根的子樹中的節點. tree_remove(root)和tree_add(root)其實就是遍歷以root爲根的子樹中的節點, 並把他們加到多重集或者從多重集中刪除.
複雜度就是, 每個節點被 remove_from_multiset 的次數是
代碼
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define M(a,b) memset(a,b,sizeof(a))
using namespace std;
const int MAXN=100007;
const int oo=0x3f3f3f3f;
typedef long long LL;
int l[MAXN], r[MAXN], sz[MAXN];
struct Edge
{
int to, ne;
}e[2*MAXN];
int head[MAXN], edgenum;
void addedge(int u, int v)
{
e[edgenum].to=v, e[edgenum].ne=head[u];head[u]=edgenum++;
e[edgenum].to=u, e[edgenum].ne=head[v];head[v]=edgenum++;
}
void dfs1(int u, int fa)//???????sz
{
sz[u]=1;
for(int i=head[u];~i;i=e[i].ne)
{
int to=e[i].to;
if(to==fa) continue;
dfs1(to, u);
sz[u]+=sz[to];
if(r[u]==0) r[u]=to;
else if(sz[to]>sz[r[u]])
l[u]=r[u], r[u]=to;
else l[u]=to;
}
}
LL sum[MAXN<<2], num[MAXN<<2];
int lowbit(int x) { return x&(-x); }
void update(LL f[], int pos, int val, int tot)
{
while(pos<=tot)
{
f[pos]+=val;
pos+=lowbit(pos);
}
}
LL query(LL f[], int pos)
{
LL ans=0;
while(pos)
{
ans+=f[pos];
pos-=lowbit(pos);
}
return ans;
}
int v[MAXN], ha[MAXN], hav[MAXN];
LL vsum[MAXN];
LL res=0;
void tree_add(int rt, int n)
{
if(rt==0) return;
res+=(query(num, n)-query(num, ha[rt]))*v[rt];
res+=query(sum, ha[rt])+v[rt];
update(num, ha[rt], 1, n);
update(sum, ha[rt], v[rt], n);
tree_add(l[rt], n), tree_add(r[rt], n);
}
void tree_rem(int rt, int n)
{
if(rt==0) return;
res-=(query(num, n)-query(num, ha[rt]))*v[rt];
res-=query(sum, ha[rt]);
update(num, ha[rt], -1, n);
update(sum, ha[rt], -v[rt], n);
tree_rem(l[rt], n), tree_rem(r[rt], n);
}
void dfs2(int n, int u)
{
if(u==0) return;
if(l[u]!=0) dfs2(n, l[u]);
if(l[u]!=0) tree_rem(l[u], n);
if(r[u]!=0) dfs2(n, r[u]);
if(l[u]!=0) tree_add(l[u], n);
res+=(query(num, n)-query(num, ha[u]))*v[u];
res+=query(sum, ha[u])+v[u];
update(num, ha[u], 1, n);
update(sum, ha[u], v[u], n);
vsum[u]=res;
}
int main()
{
int T;scanf("%d", &T);
while(T--)
{
M(l, 0), M(r, 0), M(sz, 0);
M(head, -1);edgenum=1;
M(ha, 0), M(v, 0), M(vsum, 0);
int n;scanf("%d", &n);
for(int i=1;i<=n;i++)
{
scanf("%d", &v[i]);
hav[i]=v[i];
}
sort(hav+1, hav+1+n);
int han=unique(hav+1, hav+n+1)-hav-1;
for(int i=1;i<=n;i++)
ha[i]=lower_bound(hav+1, hav+1+han, v[i])-hav;
for(int i=1;i<n;i++)
{
int u, v;scanf("%d%d", &u, &v);
addedge(u, v);
}
dfs1(1, 0);
M(num, 0), M(sum, 0);res=0;
dfs2(han, 1);
for(int i=1;i<=n;i++) printf("%lld ", vsum[i]);
printf("\n");
}
return 0;
}