CF894E
前言
我是通过 P2656 采蘑菇 来的,所以说这也是一道双倍经验题了。
思路
我们可以发现如果存在一个环,那么我们一定可以把这个环中的所有边的边权的所有值都取到(就是相当于可以将每一条边上的值都取到 \(0\)),然后我们就很容易地想到 Tarjan 缩点了,因为这样我们就可以算出每一个环中的总和,然后就是一个图了,这里我们可以用拓扑来跑一个最长路即可,但是这里有一个问题就是如何处理和,其实也很好解决,因为如果我们的 \(\sum_{i=1}^{j} i\) 是最后一个小于等于边权的,那么我们的和就是 \(val\times (i+1)-\sum_{now=1}^{i}\sum_{y=1}^{now} y\) 这个公式既然出来了,那么我们就可以通过二分来求出最大的 \(i\) 然后再通过预处理来算出要减去的值,最后直接套公式即可。
细节见代码。
代码
#include <bits/stdc++.h>
using namespace std ;
#define int long long
#define rep(i,x,y) for(int i=x;i<=y;i++)
#define rep1(i,x,y) for(int i=x;i>=y;i--)
#define fire signed
#define kong putchar(' ')
#define end putchar('\n')
#define in(x) scanf("%lld",&x)
#define lcm(x,y) x*y/__gcd(x,y)
#define il inline
#define pb push_back
#define w(x) while(x--)
il void print(int x) {
if(x>=10) print(x/10);
putchar(x%10+'0');
}
int n,m;
const int N=1e6+10;
struct node{
int x,y,z,yuan,io;
}edg[N];
int head[N],tot,cnt,sum[N];
void add(int x,int y,int z) {
edg[++tot]={y,head[x],z,x};
head[x]=tot;
}
int low[N],dfn[N],idx;
stack<int>s;
vector<pair<int,int>>v[N];
int is[N],zai[N];
void tarjan(int x) { //模板Tarjan缩点
dfn[x]=low[x]=++idx;
s.push(x);
is[x]=1;
for(int i=head[x];i;i=edg[i].y) {
int to=edg[i].x;
if(!dfn[to]) {
tarjan(to);
low[x]=min(low[x],low[to]);
}else if(is[to]) low[x]=min(low[x],dfn[to]);
}
if(low[x]==dfn[x]) {
int p;
cnt++;
do{
p=s.top();
s.pop();
zai[p]=cnt;//将每一个点所在的环记录下来
is[p]=false;
}while(p!=x);
}
}
int dis[N],vis[N];
int mp[100010],c[100100];
int in[N];
void dfs(int s) { //拓扑
dis[s]=sum[s];
queue<int>q;
rep(i,1,cnt) if(!in[i]) q.push(i);
while(q.size()) {
int x=q.front();
q.pop();
for(auto io:v[x]) {
int to=io.first,z=io.second;
if(dis[x]>=0) dis[to]=max(dis[to],dis[x]+z+sum[to]);//如果更新过
in[to]--;
if(!in[to]) q.push(to);
}
}
}
fire main() {
in(n),in(m);
rep(i,1,m) {
int x,y;
int z;
in(x),in(y),in(z);
add(x,y,z);
}
rep(i,1,50000) mp[i]=mp[i-1]+(i*(i+1)/2),c[i]=c[i-1]+i;//预处理,处理出要减的
rep(i,1,n) if(!dfn[i]) tarjan(i);
rep(i,1,m) {
int x=edg[i].yuan,y=edg[i].x;
int ip=edg[i].z;
if(zai[x]!=zai[y]) {//如果说一条边的两个点不在一个环中,那么就建边
v[zai[x]].push_back({zai[y],ip});
in[zai[y]]++;
}else {//否则算出贡献
int r=upper_bound(c+1,c+1+30000,ip)-c-1;//二分
int now=ip*(r+1);
sum[zai[x]]+=now-mp[r];//套公式
}
}
int s;
cin>>s;
memset(dis,-1,sizeof dis);
dfs(zai[s]);
int res=0;
rep(i,1,cnt) res=max(res,dis[i]);//终点不固定所以要就最大值
cout<<res<<endl;
return false;
}

浙公网安备 33010602011771号