[APIO2009]抢掠计划
【题目描述】:
抢掠计划
【思路】:
个人认为\(APIO\)考这种题完全是在搞笑,可能是怕有些选手一道题都\(A\)不了而设置的吧(比如我)。
\(tarjan\)裸题,先缩点,在\(DAG\)上跑一遍\(dp\)决策出最大利益,因为终点处必须要有酒吧。所以最后在有酒吧的点处选最大值就可以了。。
水水水。。
#include<cstdio>
#include<cstring>
#include<queue>
#define inf 0x3f3f3f3f
using namespace std;
int n,m,st,p;
const int MAXN = 500005;
struct edge{
int u,v,nxt;
}e[MAXN];int head[MAXN];int cnt = 0;int x[MAXN];int y[MAXN];int mon[MAXN];int pub[MAXN];
int dfn[MAXN];int low[MAXN];int s[MAXN];int id = 0;int tot = 0;int Bcnt = 0;bool vis[MAXN];int b[MAXN];int val[MAXN];
int dis[MAXN];bool r[MAXN];int bill[MAXN];
inline void add(int u,int v){
e[++cnt].u = u;e[cnt].v = v;e[cnt].nxt = head[u];head[u] = cnt;
}
inline void tarjan(int x){
dfn[x] = low[x] = ++id;
s[++tot] = x;vis[x] = 1;
for(int i=head[x];i;i=e[i].nxt){
int v = e[i].v;
if(!dfn[v]){
tarjan(v);
low[x] = min(low[x] , low[v]);
}
else if (vis[v]) low[x] = min(low[x] , dfn[v]);
}
if(low[x] == dfn[x]){
int j = -1;Bcnt++;
while(j ^ x){
j = s[tot--];
vis[j] = 0;
b[j] = Bcnt;
val[Bcnt] += mon[j];
if(pub[j]) bill[Bcnt] = 1;
}
}
}
queue<int>q;
inline void spfa(){
q.push(b[st]);
memset(dis,-inf,sizeof dis);dis[b[st]] = val[b[st]];
while(!q.empty()){
int u = q.front();q.pop();r[u] = 0;
for(int i=head[u];i;i=e[i].nxt){
int v = e[i].v;
if(dis[v] < dis[u] + val[v]){
dis[v] = dis[u] + val[v];
if(!r[v]){
q.push(v);
r[v] = 1;
}
}
}
}
// for(int i=1;i<=Bcnt;++i) printf("%d ",dis[i]);
int ans = -inf;
for(int i=1;i<=Bcnt;++i){
if(bill[i]) ans = max(ans , dis[i]);
}
printf("%d",ans);
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;++i){
int u,v;scanf("%d%d",&u,&v);
x[i] = u;y[i] = v;
add(u,v);
}
for(int i=1;i<=n;++i) scanf("%d",&mon[i]);
scanf("%d%d",&st,&p);
for(int i=1;i<=p;++i){
int o;scanf("%d",&o);
pub[o] = 1;
}
for(int i=1;i<=n;++i) if(!dfn[i]) tarjan(i);
memset(head,0,sizeof head);cnt = 0;
for(int i=1;i<=m;++i) if(b[x[i]] ^ b[y[i]]) add(b[x[i]] , b[y[i]]);
spfa();
return 0;
}