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;
}
posted @ 2024-01-31 11:40  highkj  阅读(3)  评论(0)    收藏  举报