记一个经典题: 找到在一颗树中,距离<(=)k的点 有多少对?

我记得大一的时候 ,听说过楼jz的男人八题,貌似这好像是其中一题。

最近做的一个题:
http://codeforces.com/contest/161/problem/D
范围: n=5e4 ,k=500
因为k 很小,所以我们可以用树形dp 解决这个问题。
dp[i][j] : i的子树 dis=j的有多少个点。
u-v
dp[u][i]+=dp[v][i-1] 但是复杂度大概有n*n 不可取啊
哦,因为 k 只有500 所以我们只需要维护前500就好了。。。。

然后还有 i 到除了i 其兄弟以及兄弟子树的 dis=k,我们再用一个dfs 解决即可,不过会重复计算,我们算两遍 然后/2即可。

using namespace std;
#define ll __int64
/*
 */

vector<int>G[50005];
ll dp[50005][505],ans;
int n,k;
void dfs(int u,int pre){
    dp[u][0]=1;
    for(int i=0;i<G[u].size();i++){
        int to=G[u][i];
        if(to==pre) continue;
        dfs(to,u);
        for(int j=1;j<=k;j++){
            dp[u][j]+=dp[to][j-1];
//          printf("dfs1:%d %d %d\n",u,j,dp[u][j]);
        }
    }
}
void dfs2(int u,int pre){
    for(int i=0;i<G[u].size();i++){
        int to=G[u][i];
        if(to==pre) continue;
        for(int j=0;j<=k;j++){
            // dp[to][j] dis=j     + dp[u][x] = k     x=k-1-j
            int x=k-1-j;
            if(x<=0) continue;  //避免和祖先重复计算
            dp[u][x] -=dp[to][x-1];  //删掉属于u子树的
            ans+=dp[to][j] * dp[u][x];  //兄弟之间是重复计算的
//          printf("%d %d  x,j :%d %d dp:%I64d %I64d\n",u,to, x,j,dp[u][x],dp[to][j]);

            dp[u][x] +=dp[to][x-1]; //....
        }
        dfs2(to,u);
    }
}
int main() {
   //freopen("1.txt","r",stdin);
    scanf("%d %d",&n,&k);
    int u,v;
    for(int i=1;i<n;i++){
        scanf("%d %d",&u,&v);
        G[u].push_back(v);
        G[v].push_back(u);
    }
    dfs(1,0);
    ans=0;
    for(int i=1;i<=n;i++)
        ans+=dp[i][k]*2;
    dfs2(1,0);
    printf("%I64d\n",ans/2);

    return 0;
}

回想起 这道题有很多变形,我打算特意找一下这道题的变形:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值