CF 1139C Edgy Trees
这个题目太长了,我就放个连接算了: http://codeforces.com/problemset/problem/1139/C
题目分析
这个题把题意读懂了就好弄了,本以为是最短路的,但应该是求连通块的。
要在一个有n个结点的树上找一个长度为k,且由结点组成的序列[a1,a2....ak],代表行路的路线a1- a2 - .... - ak , 任意两个结点之间需要用最短的距离走到(还以为要求多元最短路呢),如果在这个行走过程中经过了一条黑色边,那么这个序列就满足题目的条件,问你一共有多少这样的序列。
由于每一个序列中的元素可以重复出现,也就是说任意的ai都有n个取值,所以总序列个数为n^k个,我们应该可以想到,除去没有经过黑色边的序列,剩余的序列都可以满足条件,那我们要如何让一个序列不会经过黑色边呢?
如果我们删除了所有的黑色边,由于边的总数为n-1条,那么可能出现多个由红色边相连的连通块,那么由这个连通块内的结点组成的长度为k的序列,必然不满足条件,那我们将所有的这样的连通块全部找出,就得到了所有的不经过黑色的边的序列,最后用总序列数n^k减去这些没有经过黑色边的连通块,就是我们要求的答案了。
然后注意一些细节问题,由于我们不断地求a^k,所以添加了一个快速幂算法来加速;然后还需要用dfs求连通块的结点数.
代码区
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include <vector>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int Max = 1e6 + 10;
const int mod = 1e9 + 7;
vector<int>v[Max]; //记录红色色边
bool vis[Max]; //vis[x]记录点是否已经被访问
ll pow_fast(ll a, ll k)
{
ll ans = 1;
while (k != 0)
{
if (k & 1 != 0)ans = (ans*a) % mod;
a = a * a%mod;
k >>= 1;
}
return ans;
}
void dfs(int s,ll &ans)
{
vis[s] = true;
ans++;
for (int i = 0;i < v[s].size();i++)
{
int e = v[s][i];
if (vis[e])continue;
dfs(e,ans);
}
}
int main()
{
ll n, k;
while (scanf("%I64d%I64d", &n, &k) != EOF)
{
for(int i = 1; i <= n ; i++)
{
v[i].clear();
}
memset(vis, false, sizeof(vis));
for (int i = 1; i < n;i++)
{
int s, e, m;
scanf("%d%d%d", &s, &e, &m);
if (m)continue; //黑边不记录,这样我们就将所有的点由红边连接,导致出现多个由红边连接的连通块
v[s].push_back(e);
v[e].push_back(s);
}
ll ans = pow_fast(n, k); //总共走k个点,每个结点都是n个结点中的任意一个,故总数为n^k
for (int i = 1; i <= n; i++)
{
if (!vis[i]) //没有走过,也就是一个新的连通块
{
ll num = 0; //记录连通在一起的红色道路
dfs(i,num); //获取纯红边连接的结点
ans = (ans - pow_fast(num, k) + mod) % mod; //减去全部由红色边组成的答案
//也就是用num个点,走动k次
}
}
printf("%I64d\n", ans);
}
return 0;
}