BZOJ1179_APIO2009_抢掠计划_C++

  题目:http://www.lydsy.com/JudgeOnline/problem.php?id=1179

 

  一道用 Tarjan 缩点+SPFA 最长路的题(Tarjan 算法:http://www.cnblogs.com/hadilo/p/5889333.html )

  我们可以这样考虑,把一个连通块缩成一个点,点权为所有权值和,且只要一个点有酒吧那么该点也就有酒吧

  因为当我们进入该连通块任意一个点时,显然把它里面所有的点都抢一遍才使答案最优,而且能回到原进入点,并且只要有一个有酒吧就可以选择在那里庆祝

  缩完点后,就可以用 SPFA 求最长路,边权变为点权,因为里面已经没有环了,所以可以用

  当然最大路也可以用 Dijkstra 或 DP 等方法做

  还有这道题细节很多很多,调了我4个多小时QuQ

 1 #include<algorithm>
 2 #include<iostream>
 3 #include<cstdlib>
 4 #include<cstring>
 5 #include<cstdio>
 6 #include<cmath>
 7 #include<queue>
 8 #include<stack>
 9 #define N 500001
10 using namespace std;
11 
12 int t,first[N],next[N],back[N],last[N],v[N],a[N],dfn[N],low[N],ans,d[N];
13 bool f[N],g[N],b[N];
14 stack<int> s;
15 queue<int> q;
16 void tarjan(int x)
17 {
18     dfn[x]=low[x]=++t;
19     s.push(x);
20     int i;
21     for (i=first[x];i;i=next[i])
22         {
23             if (f[v[i]]) continue;
24             if (dfn[v[i]]) low[x]=min(low[x],dfn[v[i]]);
25             else
26                 {
27                     tarjan(v[i]);
28                     low[x]=min(low[x],low[v[i]]);
29                 }
30         }
31     if (dfn[x]==low[x])
32         {
33             int k,c=first[x];
34             while (s.top()!=x)
35                 {
36                     i=c;
37                     k=s.top();
38                     f[k]=b[k]=1;
39                     s.pop();
40                     a[x]+=a[k];
41                     g[x]|=g[k];
42                     while (next[i]) i=next[i];
43                     next[i]=first[k];
44                     c=i;
45                     for (i=last[k];i;i=back[i]) v[i]=x;
46                 }
47             f[s.top()]=1;
48             s.pop();
49         }
50 }
51 int main()
52 {
53     int n,m,i,x,k;
54     scanf("%d%d",&n,&m);
55     for (i=1;i<=m;i++)
56         {
57             scanf("%d%d",&x,&v[i]);
58             next[i]=first[x];
59             first[x]=i;
60             back[i]=last[v[i]];
61             last[v[i]]=i;
62         }
63     for (i=1;i<=n;i++)
64         {
65             scanf("%d",&a[i]);
66             g[i]=b[i]=f[i]=0;
67         }
68     scanf("%d%d",&k,&m);
69     for (i=1;i<=m;i++)
70         {
71             scanf("%d",&x);
72             g[x]=1;
73         }
74     tarjan(k);
75     for (i=1;i<=n;i++) f[i]=1;
76     d[k]=a[k];
77     q.push(k);
78     while (!q.empty())
79         {
80             k=q.front();
81             q.pop();
82             if (g[k]) ans=max(ans,d[k]);
83             for (i=first[k];i;i=next[i])
84                 {
85                     if (b[v[i]]||v[i]==k) continue;
86                     if (d[v[i]]<d[k]+a[v[i]])
87                         {
88                             d[v[i]]=d[k]+a[v[i]];
89                             q.push(v[i]);
90                         }
91                 }
92         }
93     cout<<ans<<endl;
94     return 0;
95 }

  这道题需要开无限栈,因为递归的 Tarjan 会爆栈,然而并不会手写栈

  BZOJ 上幸好开了无限栈,不然就 RE 了,不过其它的 OJ 好像没有开无限栈,RE 两个点……

  只好去网上下了一个代码,在 CodeVS 上交了一发手写栈的 Tarjan,终于A了orz(手写栈的戳这里: http://www.cnblogs.com/hadilo/p/5892791.html )

 

 

 

版权所有,转载请联系作者,违者必究

QQ:740929894

posted @ 2016-09-21 15:22  Hadilo  阅读(961)  评论(0编辑  收藏  举报