[luogu3573]RAJ-Rally

先建一个$S$和$T$,$\forall 1\le i\le n$连边$(S,i)$和$(i,T)$,则最长路即为$S到T的最长路-2$

对于这张DAG,求出一个拓扑序,点$i$为第$i$个(特别的,$id_{S}=0$且$id_{T}=n+1$),根据拓扑序的性质,对于一条路径,其$id$必然单调递增

枚举删除的点$k$,再枚举$S$到$T$的最长路上$id$中$k$的前驱后继(由于$S$和$T$,因此必然存在,强制不等于$k$),记作$i$和$j$($i$到$j$要有边),则答案为$\max(ed_{i}+st_{j}+1)-2$(分别表示从$i$到$S$和从$j$到$T$的最长路,可以预处理)

暴力枚举复杂度仍然不行,考虑删除$k-1$和删除$k$的变化,可以看作以下3步:

1.$j$的枚举范围由$[k,n]$缩小为$[k+1,n]$,将$st_{k}$向之前的贡献删去

2.查询$k$上的答案,用一个set去维护

3.$i$的枚举范围由$[1,k-1]$扩大为$[1,k]$,将$ed_{k}$向之后的贡献加入

用一个set维护插入、删除和查询最大值(注意要可重,因此删除要删指针),由于每一条边最多在左端点插入一次、右端点删除一次,总复杂度为$o(m\log_{2}m)$

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define N 500005
 4 multiset<int>s;
 5 queue<int>q;
 6 vector<int>v[N],vi[N];
 7 int n,m,x,y,r[N],id[N],st[N],ed[N],ans[N];
 8 void add(int x,int y){
 9     r[y]++;
10     v[x].push_back(y);
11     vi[y].push_back(x);
12 }
13 void del(int k){
14     assert(s.find(k)!=s.end());
15     s.erase(s.find(k));
16 }
17 int main(){
18     scanf("%d%d",&n,&m);
19     for(int i=1;i<=m;i++){
20         scanf("%d%d",&x,&y);
21         add(x,y);
22     }
23     for(int i=1;i<=n;i++){
24         add(0,i);
25         add(i,n+1);
26     }
27     x=0;
28     q.push(0);
29     while (!q.empty()){
30         int k=q.front();
31         if ((1<=k)&&(k<=n))id[++x]=k;
32         q.pop();
33         for(int i=0;i<v[k].size();i++)
34             if (--r[v[k][i]]==0)q.push(v[k][i]);
35     }
36     for(int i=1;i<=n;i++)
37         for(int j=0;j<vi[id[i]].size();j++)ed[id[i]]=max(ed[id[i]],ed[vi[id[i]][j]]+1);
38     for(int i=n;i;i--)
39         for(int j=0;j<v[id[i]].size();j++)st[id[i]]=max(st[id[i]],st[v[id[i]][j]]+1);
40     for(int i=1;i<=n;i++)s.insert(st[i]-1);
41     for(int i=1;i<=n;i++){
42         for(int j=0;j<vi[id[i]].size();j++)del(ed[vi[id[i]][j]]+st[id[i]]-1);
43         if (!s.size())ans[i]=0;
44         else ans[i]=(*--s.end());
45         for(int j=0;j<v[id[i]].size();j++)s.insert(ed[id[i]]+st[v[id[i]][j]]-1);
46     }
47     ans[0]=ans[1];
48     for(int i=2;i<=n;i++)ans[0]=min(ans[0],ans[i]);
49     for(int i=1;i<=n;i++)
50         if (ans[i]==ans[0]){
51             printf("%d %d",id[i],ans[i]);
52             return 0;
53         }
54 }
View Code

 

posted @ 2020-11-28 10:34  PYWBKTDA  阅读(122)  评论(0编辑  收藏  举报