Luogu P12683 [MX-J15-T3] 叉叉学习与自我和解 题解 [ 黄 ] [ 树论 ] [ 组合数学 ]

叉叉学习与自我和解:树基础题。

题意显然可以转化为:给定一棵有根树,有多少种加边方案使得从根到某个节点的最短路不变。

考虑如何刻画加边的限制,不难发现一条边能加当且仅当它不影响到另一节点的最短路以前从未出现过,即 \(|dep_u-dep_v|\le 1\)

于是开桶记录每个深度的节点个数,以及每个深度到下一深度的边数,那么这些边的总个数 \(sum\) 即为相同深度的横叉边、相差 \(1\) 深度且之前未出现过的横叉边的总个数。

因为每条边不会影响其他边,所以总方案数为 \(2^{sum}\)

时间复杂度 \(O(n)\)

#include <bits/stdc++.h>
#define fi first
#define se second
#define eb(x) emplace_back(x)
#define pb(x) push_back(x)
#define lc(x) (tr[x].ls)
#define rc(x) (tr[x].rs)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
using pi=pair<int,int>;
const int N=1000005,inf=0x3f3f3f3f;
const ll mod=1e9+7;
int n,dep[N],tot[N],c[N];
ll sm=0;
vector<int>g[N];
ll qpow(ll a,ll b)
{
    ll res=1;
    while(b)
    {
        if(b&1)res=(res*a)%mod;
        b>>=1;
        a=(a*a)%mod;
    }
    return res;
}
void dfs(int u,int fa)
{
    tot[dep[u]]++;
    for(auto v:g[u])
    {
        if(v==fa)continue;
        dep[v]=dep[u]+1;
        c[dep[u]]++;
        dfs(v,u);
    }
}
int main()
{
    //freopen("sample.in","r",stdin);
    //freopen("sample.out","w",stdout);
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin>>n;
    for(int i=1;i<n;i++)
    {
        int u,v;
        cin>>u>>v;
        g[u].push_back(v);
        g[v].push_back(u);
    }
    dfs(0,-1);
    for(int i=0;i<=n;i++)
    {
        sm+=(1ll*tot[i]*(tot[i]-1)/2);
        sm+=(1ll*tot[i]*tot[i+1]-c[i]);
    }
    cout<<qpow(2,sm)%mod;
    return 0;
}
posted @ 2025-06-01 17:47  KS_Fszha  阅读(13)  评论(0)    收藏  举报