Codeforces Round #548 (Div. 2) C. Edgy Trees(思维+dfs)

 

题目链接:https://codeforces.com/contest/1139/problem/C

       题意是给了一棵树,n个点,m条边。让从中选k个点,使得从a1到a2,a2到a3,ak-1到ak的路径中至少经过一条黑色的边,问这样的集合有多少个

       思路就是求他们的组合数,所有的可能就是n^k,假如说像第一个样例那样只有黑色边存在,那么不可行的情况就是下面解释的那样,所以答案就是n^k - n,如果存在红色边的情况,比如说第二个样例,因为2到3连的是红边,那么对于只有这两个点的集合来说,他们的所有情况为2^k,所以答案就是n^k - 2^k - 1(这个1是1 1 1的情况)。那么我们反过来看题中的那个图,连的有红色边的点有6个,那么我们如果让n^k - 6^k肯定是不对的,因为有些点是可以经过黑边到达另一个点的,比如从1到7,那么这样的集合是不应该减的,所以我们要减的应该是只被红边连接的点,而且必须要连通,也就是减去4^k再减去2^k,然后再减去那些剩下的一个集合中只有1个数的情况。

       所以这道题的正解应该是我们将那些连了红边的点建边,然后对于每一个点跑一边dfs,目的是为了找出与当前这个点相连的点有多少个(就是找用红边相连的联通块),然后减去这个cnt^k就好了。感觉讲的不太好理解,自己看着题上的图想一下就好了。


AC代码:

#include 
#define ll long long
#define maxn 200005
const ll mod = 1e9 + 7;
using namespace std;
struct Node{
	int to,next,w;
}Edge[maxn];
int head[maxn],num;
ll n,k,cnt;
bool vis[maxn];
map ma1,ma2;

ll ppow(ll a, ll b){
	ll sum = 1;
	a %= mod;
	while(b > 0){
		if(b % 2 == 1) sum = (sum * a) % mod;
		b /= 2;
		a = (a * a) % mod;
	}
	return sum % mod;
}

void init(){
	for(int i=0;i<=n;i++) head[i] = -1;
	num = 0;
}

void add(int u,int v,int w){
	Edge[num].to = v;
	Edge[num].w = w;
	Edge[num].next = head[u];
	head[u] = num ++;
}

void dfs(int x){
	cnt ++;
	for(int i=head[x];i!=-1;i=Edge[i].next){
		int to = Edge[i].to;
		if(!vis[to]){
			vis[to] = true;
			dfs(to);
		}
	}
}

int main()
{
  scanf("%lld%lld",&n,&k);
	init();
  for(int i=1;i

 

你可能感兴趣的:(CodeForces,ACM_搜索)