树上路径

题目描述

给定一颗n个结点的无根树,树上的每个点有一个非负整数点权,定义一条路径的价值为路径上的点权和-路径的点权最大值。
给定参数p,我们想知道,有多少不同的树上简单路径,满足它的价值恰好是p的倍数。
注意:单点算作一个路径;u ≠ v时,(u,v)和(v,u)只算一次。

题解

随便点分治
同一个分治中心按到根最大值排序,然后维护桶
裸题

#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const int maxn=100000+10;
int a[maxn],mx[maxn],d[maxn],cnt[10000000+10],size[maxn],v[maxn];
int h[maxn],go[maxn*2],next[maxn*2];
bool bz[maxn];
int i,j,k,l,t,n,m,p,tot,top;
ll ans;
int read(){
    int x=0,f=1;
    char ch=getchar();
    while (ch<'0'||ch>'9'){
        if (ch=='-') f=-1;
        ch=getchar();
    }
    while (ch>='0'&&ch<='9'){
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*f;
}
void add(int x,int y){
    go[++tot]=y;
    next[tot]=h[x];
    h[x]=tot;
}
void travel(int x,int y){
    a[++top]=x;
    int t=h[x];
    size[x]=1;
    while (t){
        if (!bz[go[t]]&&go[t]!=y) {
            travel(go[t],x);
            size[x]+=size[go[t]];
        }
        t=next[t];
    }
}
void dfs(int x,int y){
    int t=h[x];
    while (t){
        if (!bz[go[t]]&&go[t]!=y){
            mx[go[t]]=max(mx[x],v[go[t]]);
            d[go[t]]=(d[x]+v[go[t]])%p;
            dfs(go[t],x);
        }
        t=next[t];
    }
}
bool cmp(int x,int y){
    return mx[x]<mx[y]||mx[x]==mx[y]&&x<y;
}
void calc(int f,int x){
    sort(a+1,a+top+1,cmp);
    int i,j,t;
    fo(i,1,top){
        t=a[i];
        cnt[d[t]]++;
        j=(d[t]+v[x])%p;
        j=((j-mx[t])%p+p)%p;
        j=(p-j)%p;
        ans+=(ll)cnt[j]*f;
    }
    fo(i,1,top) cnt[d[a[i]]]--;
}
void solve(int x,int y){
    top=0;
    travel(x,0);
    if (y) calc(-1,y);
    int i,j=x,k=0,t;
    while (1){
        t=h[j];
        while (t){
            if (!bz[go[t]]&&go[t]!=k&&size[go[t]]>top/2){
                k=j;
                j=go[t];
                break;
            }
            t=next[t];
        }
        if (!t) break;
    }
    mx[j]=v[j];
    d[j]=0;
    dfs(j,0);
    calc(1,j);
    bz[j]=1;
    t=h[j];
    while (t){
        if (!bz[go[t]]) solve(go[t],j);
        t=next[t];
    }
}
int main(){
    freopen("path.in","r",stdin);freopen("path.out","w",stdout);
    n=read();p=read();
    fo(i,1,n-1){
        j=read();k=read();
        add(j,k);add(k,j);
    }
    fo(i,1,n) v[i]=read();
    solve(1,0);
    printf("%lld\n",ans);
}
树上路径长度统计问题通常涉及一棵由 n 个节和 n-1 条边构成的无向。每条边具有一个权重(例如长度或代价),任务可能包括计算所有节对之间的路径长度之和、找到最长路径、或计算特定条件下路径的数量或总长度等[^3]。 ### 问题描述 1. **所有节对之间的路径长度之和**:给定一棵,要求计算所有节对之间的路径长度总和。这是一个经典问题,涉及对结构的深度优先遍历(DFS)或广度优先遍历(BFS)的综合运用。 2. **最长路径的直径)**:找到中最长的路径(两个节之间的边权和最大)。 3. **特定条件下的路径统计**:例如,统计路径长度小于等于某个值的路径数量。 ### 解法分析 #### 1. 所有节对之间的路径长度之和 一种常见的解法是基于的遍历,通过计算每条边对总路径长度的贡献来求解: - 每条边将分割为两部分,一部分包含子的 s 个节,另一部分包含 n-s 个节。 - 这条边的贡献为:边权 × s × (n-s)。 - 遍历所有边,累加每条边的贡献即可得到总路径长度之和。 #### 2. 的直径(最长路径) 求解的直径通常采用两次 BFS 或 DFS: - 第一次从任意节出发,找到距离最远的节 u。 - 第二次从 u 出发,找到距离最远的节 v,u 到 v 的路径即为的直径。 #### 3. 路径数量统计 当问题涉及统计满足特定条件的路径数量时,可以使用动态规划(DP)结合深度优先搜索: - 例如,设 dp[u][i] 表示以节 u 为终路径长度为 i 的路径数量。 - 在 DFS 过程中,合并子的 DP 状态,并更新当前节的状态。 ### 示例代码 以下代码示例展示了如何计算树上所有节对之间的路径长度之和: ```cpp #include <bits/stdc++.h> using namespace std; typedef long long ll; typedef pair<int, int> pii; const int MAXN = 1005; vector<vector<pii>> adj(MAXN); ll total = 0; int n; int dfs(int u, int parent, int edge_weight) { int size = 1; for (auto v : adj[u]) { if (v.first == parent) continue; int sub_size = dfs(v.first, u, v.second); size += sub_size; total += 1LL * sub_size * (n - sub_size) * edge_weight; } return size; } int main() { cin >> n; for (int i = 0; i < n - 1; ++i) { int u, v, w; cin >> u >> v >> w; adj[u].push_back({v, w}); adj[v].push_back({u, w}); } dfs(0, -1, 0); cout << total << endl; return 0; } ``` ### 复杂度分析 - **时间复杂度**:基于 DFS/BFS 的算法通常具有 O(n) 的时间复杂度。 - **空间复杂度**:由于需要存储邻接表和递归栈,空间复杂度为 O(n)。 ### 应用场景 树上路径长度统计问题广泛应用于网络分析、通信系统优化、社交网络分析等领域。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值