[Luogu2656]采蘑菇

题目大意:
  给你一个有向图,每条边有一个边权w以及恢复系数k,
  你从s点出发乱走,经过某条边时会获得相应的收益w,而当第二次经过这条边时相应的收益为w*k下取整。
  问你最大能获得的收益为多少?

思路:
  缩点+DP。
  首先跑一下Tarjan(只要从s开始跑,因为没跑到的地方肯定和答案没关系)。
  对于每个强连通分量,我们算一下经过这个强联通分量能获得的总收益sum(就是拼命在这上面绕圈圈)。
  把原图缩为一个DAG,然后就可以DP了。
  设当前点为i,后继结点为j,边权为w,j的SCC的总收益为sum[j],转移方程为f[j]=max{f[i]+w+sum[j]}。
  当然也只要从s开始DP即可。

  1 #include<cmath>
  2 #include<stack>
  3 #include<queue>
  4 #include<cstdio>
  5 #include<cctype>
  6 #include<vector>
  7 #include<cstring>
  8 inline int getint() {
  9     register char ch;
 10     while(!isdigit(ch=getchar()));
 11     register int x=ch^'0';
 12     while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
 13     return x;
 14 }
 15 const int N=80001,M=200000;
 16 struct Edge {
 17     int from,to,w;
 18     double k;
 19     int next;
 20 };
 21 Edge e[M];
 22 int head[N];
 23 inline void add_edge(const int &u,const int &v,const int &w,const double &k,const int &i) {
 24     e[i]=(Edge){u,v,w,k,head[u]};
 25     head[u]=i;
 26 }
 27 int s;
 28 int dfn[N],low[N],scc[N],cnt,id,sum[N],in[N];
 29 bool ins[N];
 30 std::stack<int> stack;
 31 void tarjan(const int &x) {
 32     dfn[x]=low[x]=++cnt;
 33     stack.push(x);
 34     ins[x]=true;
 35     for(int i=head[x];~i;i=e[i].next) {
 36         const int &y=e[i].to;
 37         if(!dfn[y]) {
 38             tarjan(y);
 39             low[x]=std::min(low[x],low[y]);
 40         } else if(ins[y]) {
 41             low[x]=std::min(low[x],dfn[y]);
 42         }
 43     }
 44     if(dfn[x]==low[x]) {
 45         id++;
 46         int y=0;
 47         while(y!=x) {
 48             y=stack.top();
 49             stack.pop();
 50             ins[y]=false;
 51             scc[y]=id;
 52         }
 53     }
 54 }
 55 int ans,f[N];
 56 std::queue<int> q;
 57 inline void dp() {
 58     q.push(scc[s]);
 59     f[scc[s]]=sum[scc[s]];
 60     while(!q.empty()) {
 61         const int x=q.front();
 62         q.pop();
 63         ans=std::max(ans,f[x]);
 64         for(register int i=head[x];~i;i=e[i].next) {
 65             const int &y=e[i].to;
 66             f[y]=std::max(f[y],f[x]+e[i].w+sum[y]);
 67             if(!--in[y]) q.push(y);
 68         }
 69     }
 70 }
 71 int main() {
 72     const int n=getint(),m=getint();
 73     memset(head,-1,sizeof head);
 74     for(register int i=0;i<m;i++) {
 75         const int u=getint(),v=getint(),w=getint();
 76         double k;
 77         scanf("%lf",&k);
 78         add_edge(u,v,w,k,i);
 79     }
 80     s=getint();
 81     tarjan(s);
 82     memset(head,-1,sizeof head);
 83     for(register int i=0;i<m;i++) {
 84         const int &u=e[i].from,&v=e[i].to;
 85         if(!dfn[u]||!dfn[v]) continue; 
 86         if(scc[u]==scc[v]) {
 87             const double &k=e[i].k;
 88             int w=e[i].w;
 89             while(w>0) {
 90                 sum[scc[u]]+=w;
 91                 w=floor(w*k);
 92             }
 93         } else {
 94             in[scc[v]]++;
 95             add_edge(scc[u],scc[v],e[i].w,e[i].k,i);
 96         }
 97     }
 98     dp();
 99     printf("%d
",ans);
100     return 0;
101 }
原文地址:https://www.cnblogs.com/skylee03/p/7773257.html