[APIO2009]抢掠计划 解题报告
这道题码的十分痛苦。
思路:tarjan缩点,重新建图,点权转边权,边权变为负值,跑一遍spfa求最长路即可。
思路很简单,但是码量有点痛苦,打了一百行,打两个板子就发现了许多问题,例如:程序出错的时候不知道是哪里出了问题,找错十分麻烦。我自己出现的错误还是挺多的,模板不是很熟悉。
代码:
#include<cstdio> #include<iostream> #include<cstring> #include<cmath> #include<algorithm> using namespace std; const int N=500009; struct Edge{ int next,to,dis; }e[N],E[N]; int n,m,num,NUM,s,p,co,top,cnt,l,r; int head[N],far[N],HEAD[N],ans=0x3f3f3f3f,q[3*N],atm[N],flag[N],pd[N],val[N],stk[N],low[N],dfn[N],col[N],vis[N]; void add(int u,int v){ e[++num].to=v; e[num].next=head[u]; head[u]=num; } void ADD(int u,int v,int w){ E[++NUM].to=v; E[NUM].dis=w; E[NUM].next=HEAD[u]; HEAD[u]=NUM; } void tarjan(int u){ low[u]=dfn[u]=++cnt; stk[++top]=u; vis[u]=1; for(int i=head[u];i;i=e[i].next){ int v=e[i].to; if(!dfn[v]){ tarjan(v); low[u]=min(low[u],low[v]); } else if(vis[v]){ low[u]=min(low[u],dfn[v]); } } if(low[u]==dfn[u]){ col[u]=++co; vis[u]=0; val[co]+=atm[u]; while(stk[top]!=u){ col[stk[top]]=co; val[co]+=atm[stk[top]]; vis[stk[top]]=0; top--; } top--; } } void spfa(){ l=0;r=1; for(int i=1;i<=co;i++) far[i]=0x3f3f3f3f; q[1]=col[s]; far[col[s]]=-val[col[s]]; while(l<=r){ l++; int now=q[l]; pd[now]=0; for(int i=HEAD[now];i;i=E[i].next){ int v=E[i].to; if(far[now]+E[i].dis<far[v]){ far[v]=far[now]+E[i].dis; if(!pd[v]){ r++; pd[v]=1; q[r]=v; } } } } } int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=m;i++){ int u,v; scanf("%d%d",&u,&v); add(u,v); } for(int i=1;i<=n;i++) scanf("%d",&atm[i]); scanf("%d%d",&s,&p); for(int i=1;i<=p;i++){ int k; scanf("%d",&k); flag[k]=1; } for(register int i=1;i<=n;i++) if(!dfn[i])tarjan(i); for(register int u=1;u<=n;u++) for(int i=head[u];i;i=e[i].next){ int v=e[i].to; if(col[v]!=col[u]) ADD(col[u],col[v],-val[col[v]]); } spfa(); for(int i=1;i<=n;i++) if(flag[i]&&ans>far[col[i]]) ans=far[col[i]]; printf("%d",-ans); return 0; }
以前只做过 tarjan+树型dp的,这道题是tarjan + spfa 的。