1 条题解

  • 0
    @ 2026-9-24 22:24:21

    为什么没人写分治呢?

    首先肯定要拓扑排序。

    定义两端拓扑序分别为 u,v(u<v)u,v(u<v) 的边为 (u,v)(u,v),以拓扑序为 ii 的点为开头 / 结尾的最长路分别为 disi,0/1dis_{i,0/1}。

    因为要求删掉一个点后的答案,考虑缺一分治。

    对拓扑序分治,当递归到 [l,r][l,r] 时,我们需要求出 disi,1(i<l)dis_{i,1}(i<l)、disi,0(i>r)dis_{i,0}(i>r) 与当前的最长路。

    显然,当前的最长路分为左半边的最长路、右半边的最长路与跨过区间的最长路,其中左右半边的最长路可以在递推时顺便求出,跨过区间的最长路则需要枚举每一条跨过区间的边 (u,v)(u,v),用 disu,1+disv,0+1dis_{u,1}+dis_{v,0}+1 更新答案。

    设中点为 midmid,当将要递归到 [l,mid][l,mid] 时先递推求出 disi,0(i∈(mid,r])dis_{i,0}(i\in(mid,r]),然后用 disi,0(i∈(mid,r])dis_{i,0}(i\in(mid,r]) 更新单边最长路,并用边 (u,v)(u∈(0,l),v∈(mid,r])(u,v) (u\in(0,l),v\in(mid,r]) 更新跨过区间的最长路。

    递归到 [mid+1,r][mid+1,r] 的部分同理,容易证明正确性。

    因为每个点和每条边都会被遍历 O(log⁡n)O(\log n) 次,因此时间复杂度 O((n+m)log⁡n)O((n+m)\log n)。

    代码,感觉比其他做法都好写:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    int n,m,x,ma,a,ans=2147483647;
    vector<int> t[500005][2];
    queue<int> q;
    int ord[500005],id[500005],in[500005],len,dis[500005];
    int st[500005],top;
    void solve(int l,int r,int s){
    	if(l==r){
    		if(s<ans)ans=s,a=ord[l];//更新答案
    		return;
    	}
    	int ls=s,mid=(l+r)>>1;//存储开始时的答案方便还原
    	for(int i=l;i<=mid;++i){
    		for(auto j:t[ord[i]][1])dis[ord[i]]=max(dis[ord[i]],dis[j]+1),s=max(s,dis[ord[i]]);//递推求最长路并求单边答案
    		for(auto j:t[ord[i]][0])
    		if(id[j]>r)s=max(s,dis[ord[i]]+dis[j]+1);//计算跨过当前区间的答案
    	}
    	solve(mid+1,r,s);
    	s=ls;//还原答案
    	for(int i=l;i<=mid;++i)dis[ord[i]]=0;//因为这个区间之前没被用过,所以全部还原为0
    	for(int i=r;i>mid;--i){
    		for(auto j:t[ord[i]][0])dis[ord[i]]=max(dis[ord[i]],dis[j]+1),s=max(s,dis[ord[i]]);
    		for(auto j:t[ord[i]][1])
    		if(id[j]<l)s=max(s,dis[ord[i]]+dis[j]+1);
    	}
    	solve(l,mid,s);
    	for(int i=mid+1;i<=r;++i)dis[ord[i]]=0;
    }
    int main(){
    	ios::sync_with_stdio(0);cin.tie(0);
    	cin>>n>>m;
    	for(int i=1,x,y;i<=m;++i)
    		cin>>x>>y,t[x][0].emplace_back(y),t[y][1].emplace_back(x),++in[y];
    	//拓扑排序
    	for(int i=1;i<=n;++i)
    	if(!in[i])q.push(i);
    	while(!q.empty()){
    		x=q.front();q.pop();ord[++len]=x,id[x]=len;
    		for(auto i:t[x][0])
    			if(!(--in[i]))q.push(i);
    	}
    	solve(1,n,0);
    	cout<<a<<" "<<ans<<'\n';
    	return 0;
    }
    

    当然,这种做法也能做带权情况。

    • 1

    信息

    ID
    5497
    时间
    3000ms
    内存
    228MiB
    难度
    10
    标签
    递交数
    8
    已通过
    3
    上传者