洛谷 P2656 采蘑菇(Tarjan缩点,dp)
传送门
解题思路
对于每一条路,很显然只有两种情况,一种是走一次,一种是走无限次,而第二种情况的条件是这条边在一个环中。
所以先用Tarjan缩点,每个点的权值更新为这个环上所有边一直摘的蘑菇数的和。
然后跑一边既有边权也有点权的DAG上的dp即可。
//数据有锅,后四个点答案有误,用double存实际上错了,但是标程是double,为了过题只能改成double了。
具体原因请看这篇博客:关于float、double小项问题
AC代码
1 #include<iostream> 2 #include<algorithm> 3 #include<cmath> 4 #include<cstdio> 5 #include<cstring> 6 #include<stack> 7 using namespace std; 8 const int maxn=80008; 9 const int maxm=200005; 10 int n,m,scc_cnt,cnt,p[maxn][2],dp[maxn],w[maxn]; 11 int sscc[maxn],dfn[maxn],low[maxn],times,num[maxn],ss; 12 stack<int> s; 13 struct node{ 14 int v,next,value,sum; 15 }e[maxm][2]; 16 void insert(int u,int v,int value,int sum,int id){ 17 cnt++; 18 e[cnt][id].v=v; 19 e[cnt][id].value=value; 20 e[cnt][id].sum=sum; 21 e[cnt][id].next=p[u][id]; 22 p[u][id]=cnt; 23 } 24 void dfs(int u){ 25 dfn[u]=low[u]=++times; 26 s.push(u); 27 for(int i=p[u][0];i!=-1;i=e[i][0].next){ 28 int v=e[i][0].v; 29 if(!dfn[v]){ 30 dfs(v); 31 low[u]=min(low[u],low[v]); 32 }else if(num[v]==0){ 33 low[u]=min(low[u],dfn[v]); 34 } 35 } 36 if(dfn[u]==low[u]){ 37 ++scc_cnt; 38 while(1){ 39 int x=s.top(); 40 s.pop(); 41 num[x]=scc_cnt; 42 if(x==u) break; 43 } 44 } 45 } 46 void dfs2(int u){ 47 if(dp[u]) return; 48 dp[u]=sscc[u]; 49 for(int i=p[u][1];i!=-1;i=e[i][1].next){ 50 int v=e[i][1].v; 51 dfs2(v); 52 dp[u]=max(dp[u],dp[v]+e[i][1].value+sscc[u]); 53 } 54 } 55 int main() 56 { 57 memset(p,-1,sizeof(p)); 58 cin>>n>>m; 59 for(int i=1;i<=m;i++){ 60 int u,v,value,sum=0; 61 double p; 62 scanf("%d%d%d%lf",&u,&v,&value,&p); 63 int vv=value; 64 while(value>0){ 65 sum+=value; 66 value=floor((double)value*p); 67 } 68 insert(u,v,vv,sum,0); 69 } 70 cin>>ss; 71 for(int i=1;i<=n;i++) if(!dfn[i]) dfs(i); 72 cnt=0; 73 for(int u=1;u<=n;u++){ 74 for(int i=p[u][0];i!=-1;i=e[i][0].next){ 75 int v=e[i][0].v; 76 if(num[u]==num[v]){ 77 sscc[num[u]]+=e[i][0].sum; 78 }else{ 79 insert(num[u],num[v],e[i][0].value,0,1); 80 } 81 } 82 } 83 dfs2(num[ss]); 84 cout<<dp[num[ss]]; 85 return 0; 86 }