题目是求一棵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. java GUI (课堂笔记)

    关于java GUI Swing组件: JFrame 窗口组件 JLabel 标签 JButton 按钮组件 JTextField 单行文本框 系统控件(JDK自带) 自定义控件(模仿系统控件): 继 ...

  2. wap网站的优化建设

    我们在成功建立wap网站之后,不要觉得一时没有达到自己想要的效果就丢之气之,让其成为垃圾链接,我们既然前期做了大量的工作来建立起来这个网站,一定要坚持耐心的把这个网站培养下去,其实就如同我们栽种一个树 ...

  3. JAVA WEBSERVICE服务端&客户端的配置及调用(基于JDK)

    前言:我之前是从事C#开发的,因公司项目目前转战JAVA&ANDROID开发,由于对JAVA的各种不了解,遇到的也是重重困难.目前在做WEBSERVICE提供数据支持,看了网上相关大片的资料也 ...

  4. [转] 面向对象原则之GOF是招式,九大原则才是精髓

    只有到了一定层次后才会真正的深入体会到面向对象的一些知识点啊! 不谈具体程序,谈的是你对软件的理解 模式: 每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的解决方案的核心. “模式”这个 ...

  5. js对象中动态读取属性值 动态属性值 js正则表达式全局替换

    $(document).ready(function(){ var exceptionMsg = '${exception.message }'; var exceptionstr = ''; //j ...

  6. sql hibernate查询转换成实体或对应的VO Transformers

    sql查询转换成实体或对应的VO Transformers //addScalar("id") 默认查询出来的id是全部大写的(sql起别名也无效,所以使用.addScalar(& ...

  7. 【Netty】TCP粘包和拆包

    一.前言 前面已经基本上讲解完了Netty的主要内容,现在来学习Netty中的一些可能存在的问题,如TCP粘包和拆包. 二.粘包和拆包 对于TCP协议而言,当底层发送消息和接受消息时,都需要考虑TCP ...

  8. php键值相同的项数值相加

    php 合并一个二维数组相同项,数量则相加 $arr = array( array( 'user_id' => 100, 'goods_id' => 10, 'number' => ...

  9. Java 9 揭秘(6. 封装模块)

    Tips 做一个终身学习的人. 在这章节中, 主要介绍以下内容: 封装Java模块的不同格式 JAR格式增强 什么是多版本JAR 如何创建和使用多版本JAR JMOD是什么格式 如何使用jmod工具来 ...

  10. spring的Convert机制

    spring.core包内有Converter接口,方法是T convert(S source);从一个类型转为内容一个类型的实际转化器:converters可能非常多,需要一个注册器来集中管理使用, ...