E. Hammer to Fall

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} 总体时间复杂度:qblog+blogqm

当 b l o g = m 时,总体时间复杂度为 : O ( q ∗ m ) 当blog=\sqrt{m}时,总体时间复杂度为:O(q*\sqrt{m}) blog=m 时,总体时间复杂度为:O(qm )

这样的时间复杂度最坏的情况下,有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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值