[BZOJ3875]-[Ahoi2014&Jsoi2014]骑士游戏-Djikstra

说在前面

自己想出来的题,感觉果然不一样呢=w=
再麻烦也要写出来!!!


题目

BZOJ3875传送门
不是权限题,可以去B站看题


解法

自己瞎YY了一个做法,开了两个map常数大到飞起,BZOJ倒数第九美滋滋
这里写图片描述
好了来说题解吧…
注意到,如果一直选择肉搏,怪物会一直分裂,但是这样的分裂必须有个尽头。就是说,最后的怪物一定是用魔法杀死的(或者说那只怪物不会分裂,这种情况可以直接把肉搏费用 看成 法术费用)。
因此,可以立刻得出一个结论,对于法术费用最小的那只怪物,杀死它花费的最小代价就是它自己的法术费用。那么我们用这只怪去更新它影响的怪。然后再用剩下的怪物费用最小的去更新其它的,这就是一个dijkstra的过程。

我们用优先队列来优化这个过程,每次弹出的怪一定已经更新完毕(因为其它的怪被之前弹出过的怪更新了一遍,花费仍然比当前大,自然不可能用其它的怪来更新当前堆顶),所以dijkstra是正确的。

具体的:
我们对每个怪物记录两个费用,最小费用 和 肉搏费用
一开始我们把每个怪物的肉搏费用初值设置成:把这个怪物打散之后,分裂的怪物用法术杀死 所消耗的费用总和
然后最小费用=min(肉搏费用,直接法术杀死),然后分别记录下分裂的怪物对这只怪物的贡献(用map)
跑dijkstra的时候维护信息即可。具体看代码


下面是自带大常数的代码

#include <map>
#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;
// sp = split
map<int,int> sp[200005] ;
int N , R[200005] , tp , head[200005] ;
long long S[200005] , K[200005] ;
struct Path{
    int pre , to ;
}p[1000005] ;
struct Data{
    int id ;
    long long dis ;
    bool operator < ( const Data &A ) const {
        return dis > A.dis ;
    }
} ;
void In( int t1 , int t2 ){
    p[++tp] = ( Path ){ head[t1] , t2 } ; head[t1] = tp ;
}

long long dis[200005] , spdis[200005] ;
map<int,long long> spv[200005] ;
priority_queue<Data> que ;
void solve(){
    for( int i = 1 ; i <= N ; i ++ ){
        long long tmp = S[i] ;
        for( map<int,int>::iterator j = sp[i].begin() ; j != sp[i].end() ; j ++ ){
            long long c = K[ j->first ] * j->second ;
            tmp += c ; spv[i][j->first] = c ;
        } spdis[i] = tmp ;
        if( tmp <= K[i] ) dis[i] = tmp ;
        else dis[i] = K[i] ;
        que.push( ( Data ){ i , dis[i] } ) ;
    }

    while( !que.empty() ){
        Data u = que.top() ; que.pop() ;
        if( u.dis > dis[u.id] ) continue ;
        for( int i = head[u.id] ; i ; i = p[i].pre ){
            int v = p[i].to ;
            long long c = sp[v][u.id] * u.dis ;
            if( spv[v][u.id] > c ){
                spdis[v] += c - spv[v][u.id] ;
                spv[v][u.id] = c ;
                if( dis[v] > spdis[v] ){
                    dis[v] = spdis[v] ;
                    que.push( ( Data ){ v , dis[v] } ) ;
                }
            }
        }
    } printf( "%lld" , dis[1] ) ;
}

int main(){
    scanf( "%d" , &N ) ;
    for( int i = 1 ; i <= N ; i ++ ){
        scanf( "%lld%lld%d" , &S[i] , &K[i] , &R[i] ) ;
        for( int j = 1 , t ; j <= R[i] ; j ++ ){
            scanf( "%d" , &t ) ;
            if( sp[i][t] == 0 ) In( t , i ) ;
            sp[i][t] ++ ;
        }
    } solve() ;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值