luogu P4042 [AHOI2014/JSOI2014]骑士游戏
题面传送门
设\(f_i\)表示将\(i\)及其后代打死的最小代价。
可以发现转移方程是这样的\(f_u=min(b_x,a_x+\sum\limits_{v\in u}{f_v})\)
发现转移成环,不太好做。
如果我们先把所有点放入队列,然后对于每个能更新答案的点将其父节点入队,再更新答案即可。
注意卡常。特别是用vector存边和不要重复入队。
时间复杂度\(O(能过)\)
code:
#include <vector>
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<cmath>
#include<algorithm>
#include<bitset>
#define I inline
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define l(x) x<<1
#define r(x) x<<1|1
#define re register
#define ll long long
#define db long double
#define N 200000
#define eps (1e-6)
#define mod 1000000007
using namespace std;
int n,m,k,x,t,now,fl[N+5],tot;ll d[N+5],a[N+5],b[N+5],pus;
vector<int> g[N+5],fa[N+5];queue<int> q;
I void read(int &x){
char s=getchar();x=0;while(s<'0'||s>'9') s=getchar();
while(s>='0'&&s<='9') x=x*10+s-48,s=getchar();
}
int main(){
// freopen("1.in","r",stdin);
re int i;scanf("%d",&n);for(i=1;i<=n;i++) {
scanf("%lld%lld",&a[i],&b[i]);read(t);while(t--) read(x),g[i].push_back(x),fa[x].push_back(i);d[i]=b[i];q.push(i);
}
while(!q.empty()){
now=q.front();q.pop();fl[now]=0;
for(pus=i=0;i<g[now].size();i++) pus+=d[g[now][i]];if(d[now]<=pus+a[now]) continue;
for(i=0;i<fa[now].size();i++) tot=fa[now][i],!fl[tot]&&(q.push(tot),fl[tot]=1);d[now]=pus+a[now];
}
printf("%lld\n",d[1]);
}