[codeforces 894 E] Ralph and Mushrooms 解题报告 (SCC+拓扑排序+DP)
题目链接:http://codeforces.com/problemset/problem/894/E
题目大意:
$n$个点$m$条边的有向图,每条边有一个权值,可以重复走。
第$i$次走过某条边权为$w$的边后这条边的边权变成$w-i$,但不能小于等于$0$。
给定起点,询问任意走最多能获得多少的边权
题解:
显然一个强联通分量的边可以全部走到$0$为止。
考虑强连通分量中一条边权为w的边对答案的贡献,$s=w+w-1+w-1-2+w-1-2-3\ldots$
设这个式子有$t+1$项,显然有$\frac{(t+1)t}{2}<=w$,解得$t=\sqrt{2w+0.25}-0.5$,t下取整
那么$s=(t+1)w-\frac{(t+2)(t+1)t}{6}$
得到每个强连通分量之后剩下的部分拓扑排序后DP即可
#include<algorithm> #include<cstring> #include<cstdio> #include<iostream> #include<vector> #include<queue> #include<cmath> typedef long long ll; using namespace std; const int N=1e6+1000; const int MAX=1e8+15; const ll inf=1ll<<60;//这个要足够大,卡了2h... int n,m,tot,top,tim,scc,t; int sta[N],dfn[N],low[N],belong[N],deg[N],head[N]; bool ins[N]; ll dp[N],val[N],sum[N],tmp[N]; struct EDGE { int from,to,nxt;ll w; }edge[N]; struct node{int v;ll w;}; vector <node> g[N]; inline ll read() { char ch=getchar(); ll s=0,f=1; while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();}; while (ch>='0'&&ch<='9') {s=(s<<3)+(s<<1)+ch-'0';ch=getchar();} return s*f; } void add(int u,int v,int w) { edge[++tot]=(EDGE){u,v,head[u],w}; head[u]=tot; } void tarjan(int x) { dfn[x]=low[x]=++tim; ins[x]=1;sta[++top]=x; for (int i=head[x];i;i=edge[i].nxt) { int y=edge[i].to; if (!dfn[y]) { tarjan(y); low[x]=min(low[x],low[y]); } else if (ins[y]) low[x]=min(low[x],dfn[y]); } if (dfn[x]==low[x]) { ++scc;int k; do { k=sta[top--]; belong[k]=scc; ins[k]=0; } while(k!=x); } } inline ll calc(ll x) { ll tt=sqrt(2*x+0.25)-0.5; return x+tt*x-(tt+1)*(tt+2)*tt/6; } int main() { //for (t=1;tmp[t-1]<MAX;t++) {tmp[t]=tmp[t-1]+t;sum[t]=sum[t-1]+tmp[t];} n=read();m=read(); for (int i=1,u,v,w;i<=m;i++) { u=read();v=read();w=read(); add(u,v,w); } for (int i=1;i<=n;i++) if (!dfn[i]) tarjan(i); for (int i=1;i<=m;i++) { int u=edge[i].from,v=edge[i].to; u=belong[u],v=belong[v]; if (u==v) val[u]+=calc(edge[i].w); } for (int u=1;u<=n;u++) { for (int i=head[u];i;i=edge[i].nxt) { int v=edge[i].to; int bu=belong[u],bv=belong[v]; if (bu==bv) continue; g[bu].push_back((node){bv,edge[i].w+val[bv]}); deg[bv]++; } } int st=read();st=belong[st]; queue <int> q; for (int i=1;i<=scc;i++) dp[i]=-inf;dp[st]=val[st]; for (int i=1;i<=scc;i++) if (!deg[i]) q.push(i); while (!q.empty()){ int k=q.front();q.pop(); for (int i=0;i<g[k].size();i++){ int y=g[k][i].v; if (--deg[y]==0) q.push(y); dp[y]=max(dp[y],dp[k]+g[k][i].w); } } ll mx=0; for (int i=1;i<=scc;i++) mx=max(mx,dp[i]); printf("%lld\n",mx); return 0; }
星星之火,终将成燎原之势