rt,思路就是枚举每个点 x 计算所有 lca 为 x 的点对贡献, 具体实现方法为以每个 x 进行一个最短路的跑,只能经过祖先和子树内的点,建的不是反图。
第二个样例错了,比答案小,造了几组小数据没找到问题(造的太水了qwq。
#include<bits/stdc++.h>
#define ll long long
#define ls (x<<1)
#define rs (x<<1|1)
#define N 200005
#define endl "\n"
#define fi first
#define se second
using namespace std;
const ll mod=998244353;
const ll inf=1e18;
const double eps=1e-6;
ll n,m,cnt,res;
vector<pair<ll,ll> >v[N];
ll val[N],d[N][2],f[N][2],g[N][2];
ll num=0,le[N],ri[N],dis[N],fa[N],siz[N];
ll bel[N],ibel[N];
bool vis[N];
bool in(ll x,ll y){
return bel[y]>=le[x]&&bel[y]<=ri[x];
}
priority_queue<pair<ll,ll> >q;
void dfs(ll x){
siz[x]=1; //子树内点树
bel[x]=++num; //dfn编号
ibel[num]=x; //逆dfn编号
le[x]=ri[x]=num; //子树内dfn编号区间
if(rs>cnt)return ;//判断有无儿子
fa[ls]=x,fa[rs]=x;//记录父亲
dfs(ls),dfs(rs);//递归操纵
ri[x]=num;//子树内dfn编号区间
siz[x]=siz[ls]+siz[rs]+siz[x];//统计大小
d[x][0]=(d[ls][0]+(d[ls][1]+val[ls]*siz[ls]%mod)%mod)%mod;//计算左子树内点到x的距离和
d[x][1]=(d[rs][0]+(d[rs][1]+val[rs]*siz[rs]%mod)%mod)%mod;//计算右子树内点到x的距离和
for(int i=le[x];i<=ri[x];i++)dis[ibel[i]]=inf,vis[ibel[i]]=0;//最短路初始化
for(int i=x;i;i=fa[i])dis[i]=inf,vis[i]=0;
//求最短路
dis[x]=0;
q.push({0,x});
while(!q.empty()){
auto t=q.top().se;
q.pop();
if(vis[t])continue;
vis[t]=1;
for(auto [y,w]:v[t]){
if(!in(x,y)&&!in(y,x))continue;//只能走子树内和祖先
if(dis[y]>dis[x]+w){
dis[y]=dis[x]+w;
q.push({-dis[y],y});
}
}
}
for(int i=le[ls];i<=ri[ls];i++)if(dis[ibel[i]]!=inf)g[x][0]++,f[x][0]=(f[x][0]+dis[ibel[i]])%mod;//统计x到左子树内点的最短路和
for(int i=le[rs];i<=ri[rs];i++)if(dis[ibel[i]]!=inf)g[x][1]++,f[x][1]=(f[x][1]+dis[ibel[i]])%mod;//统计x到右子树内点的最短路和
res=(res+(d[x][0]+d[x][1])%mod)%mod;// x内子树所有点到 x 的最短路之和
res=(res+(f[x][0]+f[x][1])%mod)%mod;// x到x子树内的最短路之和
res=(res+g[x][0]*d[x][1]%mod)%mod;//x可以到左子树的点数 * 右子树到x的最短路和
res=(res+f[x][0]*siz[rs]%mod)%mod;//x到左子树的最短路和 * 右子树可以到x的点数
res=(res+g[x][1]*d[x][0]%mod)%mod;//x可以到右子树的点数 * 左子树到x的最短路和
res=(res+f[x][1]*siz[ls]%mod)%mod;//x到右子树的最短路和 * 左子树可以到x的点数
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
cin>>n>>m;
cnt=(1<<n)-1;
//建的是正边
for(int i=2,x;i<=cnt;i++){
cin>>x;
val[i]=x;//边权下放点权
v[i].push_back({(i>>1),x});
}
while(m--){
ll x,y,z;
cin>>x>>y>>z;
v[x].push_back({y,z});
}
dfs(1);
cout<<res<<endl;
return 0;
}