D. Maximum Distributed Tree 思维+DFS

传送门

分析

这道题分析思路很简单——统计每条边的贡献,然后把权值和贡献进行排序,乘一下贪心即可

所以这道题的难点转换成了——我们如何去统计每条边的贡献值

我一开始的思路是用前向星存图,然后每条边进行离散化,最后跑一下bfs,但这个只能统计从一个节点开始往后面走,这条路上面每条边的贡献,如果要完全统计的话就得把每个节点放入队列中,那么时间复杂度就是n平方,明显要tle

后来我们可以发现,这个图很明显是一个树的结构,而在一棵树中,任意两点之间的路径是唯一的,这一点就很有用了

我们如果要统计一条边的贡献,可以先把这条边断掉,然后就形成了两个子树,题目要求每两个点之间都要进行连接,所以这两颗子树中的所有点之间都要进行一次连接,也就是通过断掉的那条边,我们假设其中一颗子树的节点个树是m,那么这条边的贡献就是m * (n - m)
所以我们需要做的就是统计每个节点的子树大小,然后对m * (n - m)进行排序即可

最后需要注意一下权值的数量问题,如果大于n - 1 的话就需要对最大的几个进行合并,如果小的话就需要补1

代码

#include <iostream>
#include <algorithm>
#include <vector>
#include <cstring>

using namespace std;
typedef long long ll;

const int N = 1e5 + 10,mod = 1e9 + 7;
ll w[N];
vector<ll> Q;
int h[N],ne[N * 2],e[N * 2],idx;
int si[N];
int n,m;

void add(int x,int y){
    e[idx] = y,ne[idx] = h[x],h[x] = idx++;
}

void init(){
    idx = 0;
    memset(h,-1,sizeof h);
    Q.clear();
    Q.push_back(-1);
}

void dfs(int u,int f){
    si[u] = 1;
    for(int i = h[u];~i;i = ne[i]){
        int t = e[i];
        if(t == f) continue;
        dfs(t,u);
        si[u] += si[t];
        Q.push_back(1ll * (n - si[t]) * si[t]);
    }
}

int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        init();
        scanf("%d",&n);
        for(int i = 0;i < n - 1;i++){
            int x,y;
            scanf("%d%d",&x,&y);
            add(x,y);
            add(y,x);
        }
        dfs(1,0);
        scanf("%d",&m);
        for(int i = 1;i <= m;i++) scanf("%lld",&w[i]);
        if(m > n - 1){
            sort(w + 1,w + m + 1);
            w[n - 1] %= mod;
            for(int i = n;i <= m;i++) w[n - 1]  = w[n - 1] * w[i] % mod;
        }
        else{
            for(int i = m + 1;i < n;i++) w[i] = 1;
            sort(w + 1,w + n);
        }
        sort(Q.begin(),Q.end());
        ll ans = 0;
        for(int i = 1;i < n;i++) ans = (ans + w[i] * Q[i]) % mod;
        printf("%lld\n",ans);
    }
}

猜你喜欢

转载自blog.csdn.net/tlyzxc/article/details/108249643