标签:可以转化 部分 turn pop lap spl 缩点 efi sum
显然如果走到一个SCC里,那么可以把这个SCC里的权值反复走取完
连接SCC之间的边显然只能走一次(缩点后是DAG)
那么我们tarjan缩点,然后在DAG上DP,就是个带权最长路,随便DP一下就行了
边权就是w,点权需要另外计算
对每个SCC内部的边,假设边权为\(w\),那么下降次数是满足\(\frac{(t+1)*t}{2} \leq w\)的最大的\(t\),这个可以二分出来
然后考虑贡献了多少,用整体减掉下降的每种权值的贡献,实际上是\((t+1)*w-t*1-(t-1)*2-…-2*(t-1)-1*t\)
后面那部分写成和式的形式是\(\sum_{i=1}^{t}{i*(t+1-i)}\)
然后这个拆开来可以转化成等差数列和加平方和计算
1 #include<bits/stdc++.h> 2 #define ll long long 3 #define maxn 1000005 4 using namespace std; 5 int n,m,s; 6 vector< pair<int,int> > g[maxn],g2[maxn]; 7 stack<int> stk; 8 vector<int> scc[maxn]; 9 int pre[maxn],low[maxn],bel[maxn],Tim,cnt; 10 void tarjan(int u) 11 { 12 pre[u]=low[u]=++Tim; 13 stk.push(u); 14 for(auto pa:g[u]) 15 { 16 int v=pa.first; 17 if(!pre[v])tarjan(v),low[u]=min(low[u],low[v]); 18 else if(!bel[v])low[u]=min(low[u],pre[v]); 19 } 20 if(low[u]==pre[u]) 21 { 22 cnt++; 23 while(1) 24 { 25 int x=stk.top();stk.pop(); 26 bel[x]=cnt; 27 scc[cnt].push_back(x); 28 if(x==u)break; 29 } 30 } 31 } 32 ll calc(ll w) 33 { 34 ll l=0,r=20000,t=0; 35 while(l<=r) 36 { 37 ll mid=(l+r)>>1; 38 if(mid*(mid+1)<=2*w)t=mid,l=mid+1; 39 else r=mid-1; 40 } 41 return (t+1)*w-((t+1)*(t+1)*t/2-t*(t+1)*(2*t+1)/6); 42 } 43 ll we[maxn],dp[maxn]; 44 void dfs(int u) 45 { 46 if(dp[u])return; 47 dp[u]=0; 48 for(auto pa:g2[u]) 49 { 50 int v=pa.first,w=pa.second; 51 dfs(v); 52 dp[u]=max(dp[u],dp[v]+w); 53 } 54 dp[u]+=we[u]; 55 } 56 int main() 57 { 58 scanf("%d%d",&n,&m); 59 for(int u,v,w,i=1;i<=m;++i) 60 { 61 scanf("%d%d%d",&u,&v,&w); 62 g[u].push_back(make_pair(v,w)); 63 } 64 Tim=cnt=0; 65 for(int i=1;i<=n;++i)if(!pre[i])tarjan(i); 66 for(int u=1;u<=n;++u) 67 { 68 for(auto pa:g[u]) 69 { 70 int v=pa.first,w=pa.second; 71 if(bel[u]==bel[v])we[bel[u]]+=calc(w); 72 else g2[bel[u]].push_back(make_pair(bel[v],w)); 73 } 74 } 75 scanf("%d",&s); 76 dfs(bel[s]); 77 printf("%lld\n",dp[bel[s]]); 78 }
标签:可以转化 部分 turn pop lap spl 缩点 efi sum
原文地址:https://www.cnblogs.com/uuzlove/p/12294133.html