正题
题目链接:https://www.luogu.com.cn/problem/P3573
题目大意
nnn个点mmm条边的DAGDAGDAG,删掉一个点使得最长路最短。
解题思路
先跑一遍拓扑排序
dsids_idsi表示以iii结尾的最长路,dtidt_idti表示以iii开头的最长路,用拓扑序+dp可以搞定
定义两个点集SSS和TTT,我们先将所有所有点放入TTT集合,并且把dtdtdt放入一个数据结构里。
然后按照拓扑序枚举从小到大删除哪个点,枚举到的点xxx我们把dtxdt_xdtx从数据结构里删除,对于y−>xy->xy−>x我们可以把dsy+dtx+1ds_y+dt_x+1dsy+dtx+1从数据结构里删除。
然后查询最小值统计答案
之后把dsxds_xdsx和对于x−>yx->yx−>y我们有dsx+dty+1ds_x+dt_y+1dsx+dty+1都丢进数据结构里。
这里用树状数组+二分统计答案。
时间复杂度O(nlog2n)O(n\log^2 n)O(nlog2n)
codecodecode
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
#define lowbit(x) (x&-x)
using namespace std;
const int N=1e6+10;
struct node{int to,next;
}a[N];
queue<int> q;
int n,m,cnt,ans,id;
int in[N],top[N],ds[N],dt[N],ls[N];
vector<int> init[N];
struct Tree_Array{int t[N];void Change(int x,int val){if(!x) return;while(x<=m){t[x]+=val;x+=lowbit(x);}return;}int Ask(int x){int ans=0;while(x){ans+=t[x];x-=lowbit(x);}return ans;}int Maxs(){int z=Ask(m);int l=0,r=m;while(l<=r){int mid=(l+r)>>1;if(Ask(mid)==z)r=mid-1;else l=mid+1;}return l;}
}T;
void Top_Sort(){for(int i=1;i<=n;i++)if(!in[i])q.push(i),top[++cnt]=i;while(!q.empty()){int x=q.front();q.pop();for(int i=ls[x];i;i=a[i].next){int y=a[i].to;in[y]--;if(!in[y])q.push(y),top[++cnt]=y;}}return;
}
void Get_Dis(){for(int i=1;i<=n;i++){int x=top[i];for(int j=ls[x];j;j=a[j].next){int y=a[j].to;ds[y]=max(ds[y],ds[x]+1);}}for(int i=n;i>=1;i--){int x=top[i];for(int j=0;j<init[x].size();j++){int y=init[x][j];dt[y]=max(dt[y],dt[x]+1);}}return;
}
void Solve(){ans=2147483647;for(int i=1;i<=n;i++)T.Change(dt[i],1);for(int k=1;k<=n;k++){int x=top[k];T.Change(dt[x],-1);for(int i=0;i<init[x].size();i++){int y=init[x][i];T.Change(ds[y]+dt[x]+1,-1);}int z=T.Maxs();if(z<ans) ans=z,id=x;T.Change(ds[x],1);for(int i=ls[x];i;i=a[i].next){int y=a[i].to;T.Change(ds[x]+dt[y]+1,1);}}return;
}
int main()
{scanf("%d%d",&n,&m);for(int i=1;i<=m;i++){int x,y;scanf("%d%d",&x,&y);a[i].to=y;a[i].next=ls[x];ls[x]=i;in[y]++;init[y].push_back(x);}Top_Sort();Get_Dis();Solve();printf("%d %d",id,ans);
}