bzoj 3875: [Ahoi2014]骑士游戏

Description

 【故事背景】
长期的宅男生活中,JYY又挖掘出了一款RPG游戏。在这个游戏中JYY会
扮演一个英勇的骑士,用他手中的长剑去杀死入侵村庄的怪兽。
【问题描述】
在这个游戏中,JYY一共有两种攻击方式,一种是普通攻击,一种是法术攻
击。两种攻击方式都会消耗JYY一些体力。采用普通攻击进攻怪兽并不能把怪兽彻底杀死,怪兽的尸体可以变出其他一些新的怪兽,注意一个怪兽可能经过若干次普通攻击后变回一个或更多同样的怪兽;而采用法术攻击则可以彻底将一个怪兽杀死。当然了,一般来说,相比普通攻击,法术攻击会消耗更多的体力值(但由于游戏系统bug,并不保证这一点)。
游戏世界中一共有N种不同的怪兽,分别由1到N编号,现在1号怪兽入
侵村庄了,JYY想知道,最少花费多少体力值才能将所有村庄中的怪兽全部杀死呢?

Input

第一行包含一个整数N。
接下来N行,每行描述一个怪兽的信息;
其中第i行包含若干个整数,前三个整数为Si,Ki和Ri,表示对于i号怪兽,
普通攻击需要消耗Si的体力,法术攻击需要消耗Ki的体力,同时i号怪兽死亡后会产生Ri个新的怪兽。表示一个新出现的怪兽编号。同一编号的怪兽可以出现多个。

Output

 输出一行一个整数,表示最少需要的体力值。

Sample Input

4
4 27 3 2 3 2
3 5 1 2
1 13 2 4 2
5 6 1 2

Sample Output

26

HINT

【样例说明】

首先用消耗4点体力用普通攻击,然后出现的怪兽编号是2,2和3。花费

10点体力用法术攻击杀死两个编号为2的怪兽。剩下3号怪兽花费1点体力进

行普通攻击。此时村庄里的怪兽编号是2和4。最后花费11点体力用法术攻击

将这两只怪兽彻底杀死。一共花费的体力是4+5+5+1+5+6=26。

【数据范围】

2<=N<=2*10^5,1<=Ri,Sigma(Ri)<=10^6,1<=Ki,Si<=5*10^14

【JSOI2014第二轮 D1T3】
那时候做这题直接用贪心拿了30分。完全没想到正解那么巧妙的方法

令dk表示破坏第k个节点所需要的体力值
我们可以发现,当且仅当si+ sigma dj<di时更新di【j为i的后继】
这个式子很像spfa更新时候的式子。那么我们可以一开始把所有的点都加进队列里,然后如果这个点可以被后继更新,那么酒吧这个点的所有前驱都加入队列即可。
【不明真相地切了个RANK1,大概也保持不了多久】
#include<queue>
#include<cstdio>
using namespace std;
struct line
{
     int s,t;
     int next;
}a[1000001],exa[1000001];
int head[200001],exhead[200001];
int edge,exedge;
inline void add(int s,int t)
{
     a[edge].next=head[s];
     head[s]=edge;
     a[edge].s=s;
     a[edge].t=t;
}
inline void exadd(int s,int t)
{
     exa[exedge].next=exhead[s];
     exhead[s]=exedge;
     exa[exedge].s=s;
     exa[exedge].t=t;
}
long long s[200001],k[200001];
bool v[200001];
long long dis[200001];
long long adis[200001];
queue <int> Q;
inline void spfa()
{
     while(!Q.empty())
     {
          int d=Q.front();
          v[d]=false;
          Q.pop();
          int i;
          if(adis[d]+s[d]<dis[d])
          {
               for(i=exhead[d];i!=0;i=exa[i].next)
               {
               	    int t=exa[i].t;
               	    if(!v[t])
               	    {
                         v[t]=true;
                         Q.push(t);
                    }
                    adis[t]=adis[t]-dis[d]+adis[d]+s[d];
               }
               dis[d]=adis[d]+s[d];
          }
     }
}
int main()
{
//     freopen("knight.in","r",stdin);
//     freopen("knight.out","w",stdout);
     int n;
     scanf("%d",&n);
     int i,j;
     int r,x;
     for(i=1;i<=n;i++)
     {
          scanf("%lld%lld%d",&s[i],&k[i],&r);
          for(j=1;j<=r;j++)
          {
               scanf("%d",&x);
               edge++;
               add(i,x);
               exedge++;
               exadd(x,i);
          }
     }
     for(i=1;i<=n;i++)
     {
          Q.push(i);
          dis[i]=k[i];
          v[i]=true;
     }
     for(i=1;i<=n;i++)
          for(j=head[i];j!=0;j=a[j].next)
               adis[i]+=dis[a[j].t];
     spfa();
     printf("%lld\n",dis[1]);
     return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值