E. Hammer to Fall
题目连接
本题主要在意的是转移锤子落下的城市的人到其他城市的成本,由此,只需要求出所有锤子落下的城市转移到其他安全城市的最小成本,再乘上该城市的人数即可,完成求解。
如果从锤子头遍历到锤子尾,前者可能会干预后者产生影响,由此采用倒序dp。
如果全部遍历的话,时间复杂度O(q*m)最坏 差不多10^10, 预计20秒左右,显然时间不达标,好点
为了节省时间,和提高效率,采用了根号分治和时间分块的理念来进行处理。
对于度大于( blog = sqrt(m) )的节点,,只看它前blog最小成本的转移。
每当转移了blog次后,更新维护的dp数组,保证信息是最新的。
每到blog天,就使用nth_element O(n)进行更新,时间复杂度为 q*m/blog ;
总体时间复杂度
:
q
∗
b
l
o
g
+
q
∗
m
b
l
o
g
总体时间复杂度: q*blog+\frac{q*m}{blog}
总体时间复杂度:q∗blog+blogq∗m
当 b l o g = m 时,总体时间复杂度为 : O ( q ∗ m ) 当blog=\sqrt{m}时,总体时间复杂度为:O(q*\sqrt{m}) 当blog=m时,总体时间复杂度为:O(q∗m)
这样的时间复杂度最坏的情况下,有10^7.5次,小于5亿,显然在一秒以内。合情合理
#include<bits/stdc++.h>
using namespace std;
using LL = long long;
using pil = pair<int,int>;
const int lim = 1e5+10;
const LL INF = LLONG_MAX;
const LL mod = 998244353;
int a[lim];
int b[lim];
int deg[lim];
LL dp[lim];
int blo;
vector<pil> g[lim];
int main(){
int n,m,q;
cin>>n>>m>>q;
blo = sqrt(m); //求出分届点
for(int i=1;i<=n;i++) cin>>a[i];//输入人数
int u,v,w;
for(int i=1;i<=m;i++)
{
cin>>u>>v>>w;
g[u].emplace_back(v,w);
g[v].emplace_back(u,w);
deg[u]++;
deg[v]++;
}
for(int i=q;i>=1;i--) cin>>b[i]; //倒着输入锤子落下点,方便进行倒叙dp
for(int i=1;i<=n;i++)if(deg[i]>blo) //对度数大于blo的点,进行nth_element排序,按照规定的想法以该blog,分开两部分【在该lambdal表达式中,想要的是最小花费代价的转移点】
{
nth_element(g[i].begin(),g[i].begin()+blo,g[i].end(),[](const pil&x,const pil&y)
{
return dp[x.first] +x.second < dp[y.first] +y.second;
});
}
for(int i=1;i<=q;i++)
{
int x = b[i];
dp[x] = INF;
//倒叙求出每个最小的成本
for(int j=0;j<min(deg[x],blo);j++) dp[x] = min(dp[x],dp[g[x][j].first]+g[x][j].second);
//每当blog次时,更新当前最新前blog个最小成本信息。
if(i%blo==0)
{
for(int i=1;i<=n;i++)if(deg[i]>blo)
{
nth_element(g[i].begin(),g[i].begin()+blo,g[i].end(),[](const pil&x,const pil&y)
{
return dp[x.first] +x.second < dp[y.first] +y.second;
});
}
}
}
int ans = 0;
for (int i = 1; i <= n; ++i) ans = (ans + a[i] * dp[i]) % mod;
cout << ans << endl;
}