E. Hammer to Fall题解
可以想到处理每个点,每个人走的路线都是固定的,所以可以 \(dp_{i,j}\) 预处理在 \(i\) 时间开始初始在 \(x\) 点所需要的代价。
第一维可以通过倒叙转移来省掉。
则显然易见枚举所有出边中最小的 \(dp_y+w\)转移过来,但是复杂度 \(O(nq)\) 太大。
那对于度数大的点一个个枚举太慢,所有可以维护一个 multiset 来动态维护最小值。
还有一种考虑方法是根号分治,对于度数小于 \(blo\) 的点,直接暴力枚举即可,对于度数大于 \(blo\) 的点,对于时间分块,每过 \(blo\) 个时间点,对全局更新,因为 \(blo\) 个点最多修改 \(blo\) 个点,所以直接nth_element找到前 \(blo\) 小的 \(dp_y+w\)。
意思是并不需要枚举所有点来转移,只要找到前 \(blo\) 小的点转移。
#include<bits/stdc++.h>
#define ll long long
#define int ll
#define ls p<<1
#define rs p<<1|1
#define re register
#define pb push_back
#define pir pair<int,int>
#define f(a,x,i) for(int i=a;i<=x;i++)
#define fr(a,x,i) for(int i=a;i>=x;i--)
#define fi first
#define se second
#define lowbit(x) (x&-x)
using namespace std;
const int N=4e6+10;
const int M=5e6+10;
const int mod=998244353;
const int INF=1e18+7;
mt19937 rnd(251);
int n,m,q;
int people[N];
vector<pair<int,int>> g[N];
int v[N];
int boom[N];
int f[N];
vector<int> spe;
bool cmp(pair<int,int> g,pair<int,int> h){
return f[g.fi]+g.se<f[h.fi]+h.se;
}
void solve(){
cin>>n>>m>>q;
int len=sqrt(m);
for(int i=1;i<=n;i++){
cin>>people[i];
}
for(int i=1;i<=m;i++){
int x,y,w;
cin>>x>>y>>w;
g[x].push_back({y,w});
g[y].push_back({x,w});
}
for(int i=1;i<=n;i++){
if(g[i].size()>len){
spe.push_back(i);
}
}
for(int i=1;i<=q;i++){
cin>>boom[i];
}
for(int j=0;j<spe.size();j++){
int u=spe[j];
nth_element(g[u].begin(),g[u].begin()+len,g[u].end(),cmp);
}
for(int i=q;i>=1;i--){
int x=boom[i];
int mi=INF;
for(int j=0;j<min((ll)g[x].size(),len);j++){
int y=g[x][j].fi;
int w=g[x][j].se;
mi=min(mi,f[y]+w);
}
f[x]=mi;
if(i==1||i%len==0){
for(int j=0;j<spe.size();j++){
int u=spe[j];
nth_element(g[u].begin(),g[u].begin()+len,g[u].end(),cmp);
}
}
}
int ans=0;
for(int i=1;i<=n;i++){
ans+=(f[i]%mod*people[i]%mod)%mod;
ans%=mod;
}
cout<<ans<<'\n';
}
signed main(){
// freopen("kingdom3.in","r",stdin);
// freopen("a.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(nullptr);
int t=1;
// cin>>t;
while(t--){
solve();
}
return 0;
}

浙公网安备 33010602011771号