codeforeces 543D

本文介绍了一种算法,用于解决在一个拥有n个城市和n-1条双向道路的国家中,如何确定每种可能的首都位置下改善部分道路质量使得任意城市到首都路径上最多只有一条坏路的方案数量。通过预处理和递归深度优先搜索实现换根操作,确保计算效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

The country has n cities and n - 1 bidirectional roads, it is possible to get from every city to any other one if you move only along the roads. The cities are numbered with integers from 1 to n inclusive.
All the roads are initially bad, but the government wants to improve the state of some roads. We will assume that the citizens are happy about road improvement if the path from the capital located in city x to any other city contains at most one bad road.
Your task is — for every possible x determine the number of ways of improving the quality of some roads in order to meet the citizens’ condition. As those values can be rather large, you need to print each value modulo 1 000 000 007 (109 + 7).

设f[x]表示以x为根的子树恰好符合要求的方案数。
f[x]=(f[son[1]]+1)*(f[son[2]]+1)…同时题目还要求换根,所以我们记g[x]表示从x向上延伸的方案数(就是整棵树除了x的子树以外的部分),由父亲来更新儿子。假设x为第i个儿子,则g[x]=(f[son[1]]+1)(f[son[2]]+1)…(f[son[i-1]]+1)(f[son[i+1]]+1)…g[fa]
最终的答案是ans[i]=g[i]*f[i].
Tips:叶子节点的f值是1,根节点的g值是1.

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<vector>
using namespace std;
const int maxn=200000+10;
const int md=1000000007;
long long f[maxn],h[maxn],pre[maxn],suf[maxn];
int n;
vector<int> g[maxn];
void dfs1(int p)
{
  f[p]=1;
  for(int i=0;i<g[p].size();i++)
  {
    int v=g[p][i];dfs1(v);
    f[p]=(f[p]*(f[v]+1))%md;
  }
}
void dfs2(int p)
{
  if(!g[p].size()) return;
  pre[0]=1;
  for(int i=1;i<=g[p].size()-1;i++)
  {
    int v=g[p][i-1];
    pre[i]=(pre[i-1]*(f[v]+1))%md;
  }
  suf[g[p].size()-1]=1;
  for(int i=g[p].size()-2;i>=0;i--)
  {
    int v=g[p][i+1];
    suf[i]=(suf[i+1]*(f[v]+1))%md;
  }
  for(int i=0;i<g[p].size();i++)
  {
    int v=g[p][i];
    h[v]=(h[p]*suf[i]%md*pre[i]%md)%md;
    h[v]=(h[v]+1)%md;
  }
  for(int i=0;i<g[p].size();i++) dfs2(g[p][i]);
}
int main()
{

  scanf("%d",&n);
  for(int i=2;i<=n;i++)
  {
    int x;scanf("%d",&x);
    g[x].push_back(i);
  }
  dfs1(1);h[1]=1;dfs2(1);
  for(int i=1;i<=n;i++)
  {
    long long ans=h[i]*f[i]%md;
    printf("%I64d ",ans);  
  }
  return 0;
}  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值