- 题目链接 : https://nanti.jisuanke.com/t/16446
- 题意 :给定一棵带权树,求任意两边之间的最短距离和是多少。并且树的边权会发生改变,求每次改变后的距离和又是多少。
- 思路 :
对于下图所示的树
我们以边为研究对象分析。
对于 节点1和节点2之间的边 A,计算2、4、5到1或者3的距离时可以用到。用到的次数是 3 * (5-3) = 6次。即 记以2为根的子树的节点的数量为 m,则 边A被用到的次数是 m * (n-m),那么它的贡献就是 次数 * 边权。同理,可以扩展到每一个边,即 ans 为所有边的贡献之和。
例如,2到3之间的距离,是 节点 1到2的边A 的距离 + 节点 1到3的边B的距离。 这里A和B都被用了一次,那么A的所有次数和B的所有次数就是 以2为根的子树的所有节点到 1或者3 所经过的A和B的次数和。
每次改变一条边的权值,就把这条边之前的贡献给减去,然后再加上新的贡献就是ans。 - 代码 :
#include "bits/stdc++.h"
using namespace std;
#define fori(i,l,u) for(int i = l;i < u;i ++)
#define forj(j,l,u) for(int j = l;j < u;j ++)
#define mem(a,b) memset(a,b,sizeof(a))
#define F first
#define S second
#define pb push_back
#define mk make_pair
typedef long long ll;
typedef pair<string, int> ps;
typedef pair<int ,int> pi;
typedef vector<int > vi;
typedef vector<ll > vl;
typedef vector<pi > vpi;
const int maxn = 1e6 + 5;
ll n;
ll ft[maxn]; // f[i] 代表i的 父节点的编号
ll dis[maxn]; //dis[i] 表示i到i的父节点的距离
ll sum[maxn]; //问题的核心, sum[i]代表 i为根的子树一共有多少节点
ll ans = 0;
void init(){
// freopen("1.txt", "r", stdin);
cin>>n;
ft[1] = 1;
fori(i, 1, n+1){
sum[i] = 1;
}
fori(i, 1, n){
cin>>ft[i+1]>>dis[i+1];
}
for (ll i = n; i > 0; i--) {
sum[ft[i]] += sum[i]; //逆向遍历,因为i的父节点比i的下标小,所以从叶子节点开始遍历。
}
fori(i, 1, n+1){
ans += (n-sum[i]) * sum[i] * dis[i];
}
cout<<ans<<endl;
}
int main()
{
init();
ll m,a,b;
cin>>m;
fori(i, 0, m){
cin>>a>>b;
ans -= (n-sum[a]) * sum[a] * dis[a];;
dis[a] = b;
ans += (n-sum[a]) * sum[a] * dis[a];
cout<<ans<<endl;
}
return 0;
}
- 遇到的问题 :一开始以为是构造树然后dfs,不过显然很傻。两点之间的距离可以分别考虑每条边的贡献来看。
- 更新 :今天试了试一般的树形DP的方法做,就是不管它的父节点是不是直接给出,也不管特殊性,直接做。
- 代码 :
#include "bits/stdc++.h"
using namespace std;
#define mem(a,b) memset(a,b,sizeof(a))
#define fori(i,l,u) for(int i = l;i < u;i++)
#define forj(j,l,u) for(int j = l;j < u;j++)
#define pb push_back
#define mk make_pair
#define F first
#define S second
typedef long long ll;
typedef pair<string, int > ps;
typedef pair<int, int> pi;
typedef vector<int> vi;
typedef vector<ll> vl;
typedef vector<pi> vpi;
const int maxn = 1e6 + 6;
ll n,x,y,m,a,b;
ll dp[maxn];
ll sum[maxn];
ll ft[maxn];
typedef struct {
ll v;
ll w;
}node;
vector<node> tree[maxn];
void init(){
mem(dp,0);
mem(sum,0);
mem(ft,0);
}
void dfs(int cur,int ft){
sum[cur] = 1;
fori(i, 0, tree[cur].size()){
int son = tree[cur][i].v;
int len = tree[cur][i].w;
if (son == ft) {
continue;
}
dfs(son, cur);
sum[cur] += sum[son];
dp[cur] += dp[son] + sum[son]*(n-sum[son])*len;
}
}
int main()
{
init();
// freopen("1.txt", "r", stdin);
cin>>n;
fori(i, 1, n){
cin>>x>>y;
ft[i+1] = x;
node t1,t2;
t1.v = x;
t1.w = y;
t2.v = i+1;
t2.w = y;
tree[i+1].pb(t1);
tree[x].pb(t2);
}
dfs(1,1);
cout<<dp[1]<<endl;
cin>>m;
fori(i, 0, m){
cin>>a>>b;
dp[1] -= sum[a]*(n-sum[a])*tree[a][0].w;
tree[a][0].w = b;
dp[1] += sum[a]*(n-sum[a])*b;
cout<<dp[1]<<endl;
}
return 0;
}
- 遇到的问题 :稻香村里说丰年,听取WA声一片。。。原因是各种变量都用的int,后来调试了半天,和AC代码一直对测试数据,忽然醒悟,dp[i]和sum[i]应该是longlong的范围。。