[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\n",ans); 100 return 0; 101 }