题目是求一棵n节点树中对于C(n,k)颗子树,每棵子树为在n个节点中选不同的k个节点作为树的边界点,这样的所有子树共包含多少条边。

问题可以转化一下,对每一条边,不同的子树中可能包含可能不包含这条边,显然,只有子树那k个节点在该边的两侧均有分布时该边才被包含在子树中。所有边的被包含次数的和,即为answer。对于一条边的被包含次数,设该边两侧分别有a,b个节点,那么,该边被包含的次数为C(a+b,k)-C(a,k)-C(b,k)(也可以借助母函数函数求C(a,i)*C(b,k-i),i从1到min{a,b,k-1},结果一样)。

//dfs写的太搓了,调了半天才好。。。

题目链接

 #include<bits/stdc++.h>
using namespace std; typedef long long LL;
const LL mod=1e9+;
const LL M=1e5+; LL fac[]; //阶乘
LL inv_of_fac[]; //阶乘的逆元 LL qpow(LL x,LL n)
{
LL ret=;
for(; n; n>>=)
{
if(n&) ret=ret*x%mod;
x=x*x%mod;
}
return ret;
}
void init()
{
fac[]=;
for(int i=; i<=M; i++)
fac[i]=fac[i-]*i%mod;
inv_of_fac[M]=qpow(fac[M],mod-);
for(int i=M-; i>=; i--)
inv_of_fac[i]=inv_of_fac[i+]*(i+)%mod;
}
LL C(LL a,LL b)
{
if(b>a) return ;
if(b==) return ;
return fac[a]*inv_of_fac[b]%mod*inv_of_fac[a-b]%mod;
}
/////////////////////////////////////////////////////////////
vector<int> adj[M];
int vis[M];
LL n,k,ans,du[M],hh;
void init1()
{
ans=;
memset(vis,,sizeof(vis));
memset(du,,sizeof(du));
du[]=n;
hh=C(n,k);
for(int i=; i<=n; i++)
adj[i].clear();
}
LL dfs(int s)
{
if(adj[s].size()==&&s!=) return du[s]=;
if(du[s]&&s!=) return du[s];
vis[s]=;
LL ret,cnt=;
for(int i=; i<adj[s].size(); i++)
{
if(!vis[adj[s][i]])
{
// printf("%d -> %d\n",s,adj[s][i]);
cnt+=dfs(adj[s][i]);
ans=(ans+hh-C(dfs(adj[s][i]),k)-C(n-dfs(adj[s][i]),k))%mod;
}
}
return du[s]=cnt+;
} int main()
{
init();
while(~scanf("%lld%lld",&n,&k))
{
init1();
for(int i=; i<n; i++)
{
LL u,v;
scanf("%d%d",&u,&v);
adj[u].push_back(v);
adj[v].push_back(u);
}
dfs();
// for(int i=1; i<=n; i++)
// printf("%d:%lld=========\n",i,du[i]);
// for(int i=1; i<=n; i++)
// {
// printf("i=%d:\n",i);
// for(int j=0; j<adj[i].size(); j++)
// printf("%d ",adj[i][j]);
// puts("");
// }
printf("%lld\n",(ans+mod)%mod);
}
}

// 2017.8.15 更

回头翻一下之前自己写的博客,发现连个dfs都写这么挫,就算这样居然也有人看。重新改了一下代码贴在下面。

#include<bits/stdc++.h>
using namespace std; typedef long long LL;
const LL mod=1e9+;
const LL M=1e5+; LL fac[M+]; //阶乘
LL inv_of_fac[M+]; //阶乘的逆元 LL qpow(LL x,LL n)
{
LL ret=;
for(; n; n>>=)
{
if(n&) ret=ret*x%mod;
x=x*x%mod;
}
return ret;
}
void init()
{
fac[]=;
for(int i=; i<=M; i++)
fac[i]=fac[i-]*i%mod;
inv_of_fac[M]=qpow(fac[M],mod-);
for(int i=M-; i>=; i--)
inv_of_fac[i]=inv_of_fac[i+]*(i+)%mod;
}
LL C(LL a,LL b)
{
if(b>a) return ;
if(b==) return ;
return fac[a]*inv_of_fac[b]%mod*inv_of_fac[a-b]%mod;
}
/////////////////////////////////////////////////////////////
vector<int> adj[M];
LL n,k,ans,hh;
void init1()
{
ans=;
hh=C(n,k);
for(int i=; i<=n; i++)
adj[i].clear();
} LL dfs(int s,int pre)
{
LL ret=;
for(int i=; i<adj[s].size(); i++)
{
if(adj[s][i]==pre) continue;
LL t=dfs(adj[s][i],s);
ret+=t;
ans=(ans+hh-C(t,k)-C(n-t,k))%mod;
}
return ret;
} int main()
{
init();
while(~scanf("%lld%lld",&n,&k))
{
init1();
for(int i=; i<n; i++)
{
LL u,v;
scanf("%d%d",&u,&v);
adj[u].push_back(v);
adj[v].push_back(u);
}
dfs(,-);
printf("%lld\n",(ans+mod)%mod);
}
}

51nod_1677:treecnt的更多相关文章

  1. treecnt

    treecnt  ﹡    LH (命题人)   基准时间限制:1 秒 空间限制:131072 KB 分值: 40 给定一棵n个节点的树,从1到n标号.选择k个点,你需要选择一些边使得这k个点通过选择 ...

  2. 树上统计treecnt(dsu on tree 并查集 正难则反)

    题目链接 dalao们怎么都写的线段树合并啊.. dsu跑的好慢. \(Description\) 给定一棵\(n(n\leq 10^5)\)个点的树. 定义\(Tree[L,R]\)表示为了使得\( ...

  3. [洛谷U40581]树上统计treecnt

    [洛谷U40581]树上统计treecnt 题目大意: 给定一棵\(n(n\le10^5)\)个点的树. 定义\(Tree[l,r]\)表示为了使得\(l\sim r\)号点两两连通,最少需要选择的边 ...

  4. 1677 treecnt(贡献)

    1677 treecnt 基准时间限制:1 秒 空间限制:131072 KB 分值: 40 难度:4级算法题 给定一棵n个节点的树,从1到n标号.选择k个点,你需要选择一些边使得这k个点通过选择的边联 ...

  5. treecnt 算法马拉松20(告别美国大选及卡斯特罗)

    treecnt 基准时间限制:1 秒 空间限制:131072 KB 给定一棵n个节点的树,从1到n标号.选择k个点,你需要选择一些边使得这k个点通过选择的边联通,目标是使得选择的边数最少. 现需要计算 ...

  6. 51Nod 1677 treecnt

    一道比较基础的计数题,还是一个常用的单独计算贡献的例子. 首先看题目和范围,暴力枚举肯定是不可行的,而且\(O(n\ logn)\)的算法貌似很难写. 那我们就来想\(O(n)\)的吧,我们单独考虑每 ...

  7. 51nod 1677 treecnt(思维)

    题意: 给定一棵n个节点的树,从1到n标号.选择k个点,你需要选择一些边使得这k个点通过选择的边联通,目标是使得选择的边数最少. 现需要计算对于所有选择k个点的情况最小选择边数的总和为多少. 考虑每条 ...

  8. 【51nod1677】treecnt(树上数学题)

    点此看题面 大致题意: 给你一个节点从1~n编号的树,让你从中选择k个节点并通过选择的边联通,且要使选择的边数最少,让你计算对于所有选择k个节点的情况最小选择边数的总和. 题解 这道题乍一看很麻烦:最 ...

  9. 【计数】51nod1677 treecnt

    要将答案看做是小问题的贡献和 Description 给定一棵n个节点的树,从1到n标号.选择k个点,你需要选择一些边使得这k个点通过选择的边联通,目标是使得选择的边数最少. 现需要计算对于所有选择k ...

随机推荐

  1. openjdk7之编译和debug

    大家也可以看我的博客: openjdk7之编译和debug,这里格式更好. 为了更好的学习JDK.HotSpot等源码,需要能debug JDK.HotSpot等源码.本文主要讲述,怎么编译open ...

  2. HTML 简单了解

    HTML 特别的通俗易懂!想学自己制作网页的,就来我这看看吧! 首先 我先介绍一下什么是HTML! HTML是用来描述网页的一种语言!他结合CSS样式之后会有非常炫酷的样式! 1.HTML是指一种超文 ...

  3. cal日历工具的用法

    cal的基本语法:$ cal [month] [year] 1.显示当前月的日历 $ cal 2.显示某年的日历 $ cal 2015 3.显示某年某月日历 $ cal 12 2015 =-=-=-= ...

  4. 手动添加git到目录右键菜单

      1.通过在"运行"中输入'regedit',打开注册表. 2.找到[HKEY_CLASSES_ROOT\Directory\Background]. 3.在[Backgroun ...

  5. android下的名词/片段解释

    关于建项目时候SDK解释 minimum required SDK: 又为miniSdk, 是你程序最低支持的SDK版本,这个现在一般miniSDK设定一般为8或者10 Target SDK:是你程序 ...

  6. [1] Entity Framework / Code First

    CodeFirst是EntityFramework的一种技术手段,因为传统编程方式都是先建立数据库,然后根据数据库模型为应用程序建模,再进行开发:CodeFirst从字面上理解就是代码先行,先在程序中 ...

  7. Linux 进程,线程 -- (未完)

    系统调用 Linux 将系内核的功能接口制作成系统调用, Linux 有 200 多个系统调用, 系统调用是操作系统的最小功能单元. 一个操作系统,以及基于操作系统的应用,都不能实现超越系统调用的功能 ...

  8. react-native 学习 ----- React Navigation

    很久没有的登陆博客园了,密码都是找回的,从当年的大学生已经正常的走上了程序员的道路,看到之前发的博客还是写的android,现在自己已经在使用了react-native了. 大学毕业了,做了java后 ...

  9. 各开放平台API接口通用SDK序列文章 前言

    最近两年一直在做API接口相关的工作,在平时工作中以及网上看到很多刚接触API接口调用的新人一开始会感到很不适应,要看的文档一大堆,自己要调用的接口找不着,或都找着了不知道怎么去调用,记得包括自己刚开 ...

  10. python flask(多对多表查询)

    我们在flask的学习中,会难免遇到多对多表的查询,今天我也遇到了这个问题.那么我想了好久.也没有想到一个解决的办法,试了几种方法,可能是思路的限制我放弃了,后来,我就在网上百度,可是发现百度出来的结 ...