传送门
题意: 给定一个n, 那么1-n是一个排列, 定义某个排列(x y z)的值为x - y, x - z在树上的最短距离和, 求这个排列的全排列的值相加等于多少, 树的形态会给出.
思路: 算边的贡献, 首先对于任意一个排列, 它是树上某点到其他点的距离之和, 然后由于全排列, 实际上就是树上任意两点之间的距离之和(考虑顺序), 首先树上任意两点之间的和我们会求(考虑每条边的贡献, 即边的两边的端点数量)
int siz[maxn]; ll ans = 0;
void dfs(int u, int fa) {
siz[u] = 1;
for (int i = 0 ; i < sz(ve[u]) ; ++ i) {
int to = ve[u][i];
if (to == fa) continue;
dfs(to, u);
ans += 1ll * (n - siz[to]) * siz[to] * 2;// (* len) 如果有边长的话
siz[u] += siz[to];
}
}
那么这里的多的排列, 实际上又多边的贡献有那些影响了?观察任意一个排列可知, 固定第一个点不动, 那么第一个点对后面的点总的贡献就是(n-1)!, 即后面n-1个点全排列都要算进去, 所以这道题就是在上面代码的基础上最后在乘一个(n-1)!就是答案了…
AC Code
const int maxn = 1e5+5;
struct node {
int to, next; ll w;
}e[maxn<<1];
int cnt, head[maxn];
ll fac[maxn];
void init2() {
fac[0] = 1;
for (int i = 1 ; i < maxn ; i ++) {
fac[i] = fac[i-1] * i % mod;
}
}
void init() {
cnt = 0; Fill(head, -1);
}
void add(int u, int v, ll w) {
e[cnt] = node{v, head[u], w};
head[u] = cnt++;
}
int siz[maxn], n; ll ans;
void dfs(int u, int fa) {
siz[u] = 1;
for (int i = head[u]; ~i ; i = e[i].next) {
int to = e[i].to;
if (to == fa) continue;
dfs(to, u);
ans += (1ll*n-siz[to])*1ll*siz[to]%mod*2*e[i].w%mod;
ans %= mod;
siz[u] += siz[to];
}
}
void solve() {
init2();
while(~scanf("%d", &n)) {
init();
for (int i = 1 ; i < n ; i ++) {
int u, v; ll w;
scanf("%d%d%lld", &u, &v, &w);
add(u, v, w); add(v, u, w);
}
ans = 0; dfs(1, -1);
ans = ans*fac[n-1]%mod;
printf("%lld\n", ans);
}
}