闪烁魔法(blink)

**闪烁魔法(blink)**
**【题目背景】** $Nancy$是一名清华大学的魔法师,她最擅长的魔法是闪烁魔法。 **【题目描述】** 清华大学共有$n$个地点,编号从$1$至$n$。$Nancy$的闪烁魔法可以让自己从某个地点瞬间移动到另一个地点。每次使用闪烁魔法都需要消耗一定的魔法值。在不同的两个地点之间使用闪烁魔法进行移动需要消耗的魔法值可能不同。 利用闪烁魔法,$Nancy$在清华大学生活得十分自在。然而,好景不长,由于黑恶势力的封印,$Nancy$的闪烁魔法不能再肆无忌惮地使用了。具体来说,$Nancy$只能在特定的$n−1$对地点之间使用闪烁魔法。这些特定的地点对、以及在每对地点对之间使用闪烁魔法需要消耗的魔法值会给出。 巧妙的是,$Nancy$发现,虽然黑恶势力对她进行了封印,但是她还是可以从任意一个地点出发,只使用闪烁魔法到达其他所有的地点。 $Nancy$决定不向黑恶势力低头。现在,$Nancy$要执行一个任务。在清华大学的$n$个地点中有$k$个插有旗帜的地点。而$Nancy$的任务正是访问所有插有旗帜的地点。除此之外,任务还要求$Nancy$在结束时回到她的出发点。(出发点可以任选) $Nancy$知道所有的插有旗帜的地点。由于$Nancy$聪明绝顶,所以她一定会选择魔法值消耗最小的行动方案。 作为$Nancy$的粉丝,$Yazid$想知道$Nancy$会消耗多少魔法值。但$Yazid$并不知道哪些地点有旗帜。于是他只好退而求其次,假设所有插旗帜的方案是等概率的,尝试求解$Nancy$消耗魔法值的期望值。(即所有可能情况消耗魔法值的平均值) 可惜的是,$Yazid$实在是太弱了。你能帮帮他吗? **【输入格式】** 从文件$blink.in$中读入数据。 本题包含多组数据,第一行一个正整数$ T$,表示数据组数。接下来依次描述每组数据,对于每组数据: 第一行$ 2 $个正整数$ n$,$ k$,分别表示清华大学的地点数目,以及插有旗帜的地点的数目。 接下来$ n − 1 $行,每行$ 3 $个正整数$ u,v,w$,表示一组没有被封印的闪烁魔法为:消耗$ w $点魔法值从$ u$闪烁至$ v$或从$ v $闪烁至$ u$)。 **【输出格式】** 输出到文件$ blink.out $中。 对于每组数据,输出一行一个正整数,表示期望消耗的魔法值在模$ 998244353 $意义下的结果。 **【样例$1$输入】** $1$ $3$ $2$ $1$ $2$ $1$ $2$ $3$ $3$ **【样例$1$输出】** $332748123$ **【样例$2$】** 见选手目录下的$ blink/blink2.in $与$ blink/blink2.ans$。该样例的数据规模与第$10 $个测试点相同。 **【提示】** 对于分数$ \frac{u}{v}$,其在模$ P $意义下的值$ ans $是指满足$ u ≡ v × ans (mod P) $的数。可以证明,如果$ P = 998244353$,则对于任意满足$ v $不是$ P $的倍数的分数都有$ ans $唯一。 此外,根据费马小定理,如果$ P $是质数,$ a $不是$P $的倍数,有$ a^{P−1} = a × a^{P−2 }≡ 1 (mod P)$。 **【子任务】** 对于$ 20\%$的数据,保证$ n ≤ 20$。 对于另外$ 20\%$ 的数据,保证$ k = 2$。 对于另外$ 20\%$ 的数据,保证$ k = n$。 对于另外$ 20\% $的数据,保证$ n ≤ 200$。 对于$ 100\%$ 的数据,保证$ 1 ≤ k ≤ n ≤ 100000, T ≤ 4, w ≤ 10000$。 **题解:** 我们可以发现每次走的时候每条边要么被经过$2$次,要么被经过$0$次。 根据这一点,枚举每条边,设边两端的$size$分别为$x$和$y$。($x≥1,y≥1$) 这条边对答案的贡献我们只需要用$1$减去所有点都在同一边的概率即可。 $$ans+=len*(1-\frac{C_x^k+C_y^k}{C_n^k})$$ 用费马小定理求逆元。 $Code:$
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<cstdlib>
#define N 100005
#define mod 998244353
#define ll long long
using namespace std;
int T,n,k,u,v,w,tot,head[N*4],size[N],vis[N],dist[N];
ll ans,fac[N],inv[N];
struct node
{
	int vet,next,len,u;
}edge[N*4];
void add(int u,int v,int w)
{
	edge[++tot].vet=v;
	edge[tot].next=head[u];
	head[u]=tot;
	edge[tot].len=w;
	edge[tot].u=u;
}
void dfs(int u,int dis)
{
	size[u]=1;
	vis[u]=false;
	dist[u]=dis;
	for(int i=head[u];i!=-1;i=edge[i].next)
	{
		int v=edge[i].vet;
		if(vis[v])
		{
			dfs(v,dis+1);
			size[u]+=size[v];
		}
	}
}
ll pow(ll a,int b)
{
	ll ans=1,p=a;
	while(b)
	{
		if(b%2==1)ans=(ans*p)%mod;
		p=(p*p)%mod;
		b/=2;
	}
	return ans;
}
ll calc(int n,int m)
{
	if(m>n)return 0;
	return ((fac[n]*inv[n-m])%mod*inv[m])%mod;
}
int main()
{
	freopen("blink.in","r",stdin);
	freopen("blink.out","w",stdout);
	fac[0]=1;inv[0]=1;
	for(int i=1;i<=N;i++)fac[i]=(fac[i-1]*i)%mod;
	for(int i=1;i<=N;i++)inv[i]=pow(fac[i],mod-2);
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d%d",&n,&k);
		ans=0;tot=0;
		memset(head,-1,sizeof(head));
		for(int i=1;i<n;i++)
		{
			scanf("%d%d%d",&u,&v,&w);
			add(u,v,w);add(v,u,w);
			ans=(ans+w*2)%mod;
		}
		if(n==k)
		{
			printf("%d\n",ans);
			continue;
		}
		memset(vis,true,sizeof(vis));
		dfs(1,0);
		ans=0;
		for(int u=1;u<=n;u++)
		{
			for(int i=head[u];i!=-1;i=edge[i].next)
			{
				int v=edge[i].vet,u1=u;
				if(dist[u1]<dist[v])swap(u1,v);
				ans=(ans+(((calc(n,k)-calc(size[u1],k)-calc((n-size[u1]),k))%mod+mod)%mod)*edge[i].len)%mod;
			}
		}
		ll p=calc(n,k);
		printf("%lld\n",(ans*pow(p,mod-2))%mod);
	}
	return 0;
}
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JackflyDC

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值