说在前面
自己想出来的题,感觉果然不一样呢=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() ;
}