: 永琳的竹林迷径
题目描述
竹林可以看作是一个
个点的树,每个边有一个边长
,其中有
个关键点,永琳需要破坏这些关键点才能走出竹林迷径。
然而永琳打算将这 个点编号记录下来,然后随机排列,按这个随机的顺序走过 个点,但是两点之间她只走最短路线。初始时永琳会施展一次魔法,将自己传送到选定的k 个点中随机后的第一个点。
现在永琳想知道,她走过路程的期望是多少,答案对 取模。
输入
第一行一个数
,表示测试点编号。(样例的编号表示其满足第
个测试点的性质)
下一行一个 ,表示树的点数。
下面 行,每行三个数 ,表示一条边连接 和 ,长度为 。
下面一行一个数 ,表示关键点数。
下面一行 个数,表示 个关键点的编号。
输出
一行一个数,表示答案(对
取模)。
样例输入
1
3
1 2 1
1 3 2
3
1 2 3
样例输出
4
提示
数据范围
对于 的数据,保证
。
题解:
首先我们考虑相邻两个关键点会产生的贡献。
考虑某两个点,确定一个位置后,另一个点会有
种取值。
而对于这个点,就会有
种位置,所以总共对于某两个点就是共有
种方案。
而对于除了开头结尾,产生贡献的只有两个位置,即
,加上开头结尾各一个,所以概率即为
所以对于总的概率,就是
再乘上这两个点之间的路径长,就成了期望。
由于期望的和会等于和的期望,提公因式即可得答案:
对于求两两关键点之间的路径,一般会想到使用点分治来暴力求出。
但可能过不去,于是就想到考虑每条边的贡献:
每条边会被使用的次数就是这条边两边关键点数的乘积:
对于第
条边:
直接
一遍求解、
#include<bits/stdc++.h>
using namespace std;
#define in inline
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define repd(i,a,b) for(int i=a;i>=b;i--)
#define For(i,a,b) for(int i=a;i<b;i++)
#define _(d) while(d(isdigit(ch=getchar())))
template<class T>in void g(T&t){T x,f=1;char ch;_(!)ch=='-'?f=-1:f;x=ch-48;_()x=x*10+ch-48;t=f*x;}
typedef long long ll;
const ll mod=998244353;
const int N=1e6+3;
struct E{int to,nxt;ll w;}e[N<<1];
ll head[N],tot,n,sz[N],dis[N];ll ans,k;int vis[N];
in void ins(int x,int y,ll z){
e[++tot]={y,head[x],z};head[x]=tot;
}
in void dfs(int x,int fa){
for(int i=head[x];i;i=e[i].nxt){
if(e[i].to==fa) continue;
dfs(e[i].to,x);
sz[x]+=sz[e[i].to];
ans+=e[i].w*sz[e[i].to]%mod*(k-sz[e[i].to])%mod;
ans%=mod;
}
}
in ll qp(ll x,ll y){
ll res=1;
while(y){
if(y&1) res=res*x%mod;
x=x*x%mod;y>>=1;
}return res%mod;
}
ll f[N];
int main(){
// freopen("path.in","r",stdin);freopen("path.out","w",stdout);
g(n);g(n);
For(i,1,n){
int x,y;ll z;g(x),g(y),g(z);
ins(x,y,z);ins(y,x,z);
}
g(k);
if(k==1){printf("0");return 0;}
rep(i,1,k){
int x;g(x);
sz[x]=1;
}
dfs(1,0);
ans=ans*qp(k,mod-2)%mod*2%mod;
printf("%lld\n",(ans%mod+mod)%mod);
return 0;
}