题目描述

  “最短的捷径就是绕远路,绕远路就是我最短的捷径”
  转眼就$Stage\ X$了,$Stage\ X$的比赛路线可以看做一个$n$个点$m$条边的有向无环图,每条边长度都是$1$。杰洛$\cdot$齐贝林会选择走最长的那一条路径。
  迪亚哥$\cdot$布兰度决定摧毁一个城市以及所有关于该城市的边,由于变成恐龙后脑子有点问题,他想要让摧毁后的$Stage$最长路径最短,他想知道要摧毁哪个城市,及摧毁后最长路径的长度,如果有多个城市答案相同,则输出编号最小的那一个。


输入格式

  本题包含多组数据,输入第一行一个整数$T$代表数据组数
  每组数据第一行两个整数$n,m$表示点数,边数。
  每组数据第$2\sim m+1$行每行两个整数$x_i,y_i$表示有一条连接$x_i,y_i$的边。


输出格式

  对于每组数据,输出一行两个整数,表示删除的城市编号及删除该城市后最长路径的长度。


样例

样例输入:

1
6 5
1 3
1 4
3 6
3 4
4 5

样例输出:

1 2


数据范围与提示

对于所有数据,满足$T\leqslant 10,1\leqslant n\leqslant 100,000,0\leqslant m\leqslant 500,000$。


题解

$Dijkstra$不能跑最长路!!!

解释一下。

众所周知$Dijkstra$不能跑带负边权的最短路,而跑最长路也就相当于是跑带负边权的最短路,所以它死了……

那么回来考虑这道题。

对于$DAG$,首先想到拓扑。

不妨先跑正反拓扑计算出$dis_s$和$dis_t$分别表示正反拓扑的最长路,思想类似$DP$。

那么边$i$对答案的贡献就是$dis_{s_{i_u}}+dis{t_{i_v}}+1$,删除一个点就相当与删掉了与它相连的边。

快速修改用线段树就好啦。

时间复杂度:$\Theta(m\log n)$。

期望得分:$100$分。

实际得分:$100$分。


代码时刻

#include<bits/stdc++.h>
#define L(x) x<<1
#define R(x) x<<1|1
using namespace std;
struct rec{int nxt,to;}e[1000001];
int head[2][100001],cnt;
int n,m;
int a[100001];
int dis[2][100001],sum[100001],tr[400001],in[100001],ou[100001];
pair<int,int> ans;
queue<int> q;
void pre_work()
{
memset(head,0,sizeof(head));
memset(dis,0,sizeof(dis));
memset(sum,0,sizeof(sum));
memset(tr,0,sizeof(tr));
memset(in,0,sizeof(in));
memset(ou,0,sizeof(ou));
memset(a,0,sizeof(a));
cnt=0;ans=make_pair(1,n);
}
void add(bool id,int x,int y)
{
e[++cnt].nxt=head[id][x];
e[cnt].to=y;
head[id][x]=cnt;
}
void pushup(int x){tr[x]=max(tr[L(x)],tr[R(x)]);}
void add(int x,int l,int r,int k)
{
if(l==r)
{
sum[l]++;
if(sum[l]>0)tr[x]=l;
else{sum[l]=0;tr[x]=-1;}
return;
}
int mid=(l+r)>>1;
if(k<=mid)add(L(x),l,mid,k);
else add(R(x),mid+1,r,k);
pushup(x);
}
void del(int x,int l,int r,int k)
{
if(l==r)
{
sum[l]--;
if(sum[l]>0)tr[x]=l;
else{sum[l]=0;tr[x]=-1;}
return;
}
int mid=(l+r)>>1;
if(k<=mid)del(L(x),l,mid,k);
else del(R(x),mid+1,r,k);
pushup(x);
}
int main()
{
freopen("johnny.in","r",stdin);
freopen("johnny.out","w",stdout);
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
pre_work();
for(int i=1;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
add(0,x,y);in[y]++;
add(1,y,x);ou[x]++;
}
for(int i=1;i<=n;i++)if(!in[i]){a[++a[0]]=i;q.push(i);}
while(q.size())
{
int x=q.front();q.pop();
for(int i=head[0][x];i;i=e[i].nxt)
{
if(dis[0][e[i].to]<dis[0][x]+1)dis[0][e[i].to]=dis[0][x]+1;
in[e[i].to]--;
if(!in[e[i].to]){a[++a[0]]=e[i].to;q.push(e[i].to);}
}
}
for(int i=1;i<=n;i++)if(!ou[i])q.push(i);
while(q.size())
{
int x=q.front();q.pop();
for(int i=head[1][x];i;i=e[i].nxt)
{
if(dis[1][e[i].to]<dis[1][x]+1)dis[1][e[i].to]=dis[1][x]+1;
ou[e[i].to]--;
if(!ou[e[i].to])q.push(e[i].to);
}
}
for(int i=1;i<=n;i++)add(1,0,n,dis[1][i]);
for(int x=1;x<=n;x++)
{
for(int i=head[1][a[x]];i;i=e[i].nxt)del(1,0,n,dis[0][e[i].to]+dis[1][a[x]]+1);
del(1,0,n,dis[1][a[x]]);
if(tr[1]<ans.second||(ans.second==tr[1]&&a[x]<ans.first))ans=make_pair(a[x],tr[1]);
for(int i=head[0][a[x]];i;i=e[i].nxt)add(1,0,n,dis[0][a[x]]+dis[1][e[i].to]+1);
add(1,0,n,dis[0][a[x]]);
}
printf("%d %d\n",ans.first,ans.second);
}
return 0;
}

rp++

最新文章

  1. 关于Visual Studio 未能加载各种Package包的解决方案
  2. OC中的protocol
  3. Glusterfs分布式存储介绍(一)
  4. stdout( 标准输出流)和 stderr( 标准输入流) 重定向问题
  5. [JS] javascript基础语法
  6. ipset高大上性能果断将nf-HiPac逼下课
  7. 对于mariadb安装后可以默认使用无密码登录的问题解决方案
  8. Flume思维导图
  9. 使用代理创建连接池 proxyPool
  10. git初学
  11. 解析Resources.arsc
  12. Go语言之进阶篇mysql增 删 改 查
  13. Python之包管理工具
  14. 2018/03/26 每日一个Linux命令 之 du
  15. STAX项目结束总结
  16. json&amp;pickle
  17. JIRA使用方法,简易图解
  18. JDBC操作数据库的批处理
  19. 笔试题之j2ee
  20. UrlConnection的代理和返回状态码的问题

热门文章

  1. python 运算和流程控制
  2. node工具之pm2
  3. bzoj 3837 pa2013 Filary
  4. node-images Windows 64-bit with Unsupported runtime 错误解决办法 及 node 历史版本下载
  5. Tcp之心跳包
  6. SVM处理多分类问题
  7. Linux版本显示和区别32位还是64位系统
  8. 初识linux内核漏洞利用
  9. PAT Basic 1020 月饼 (25 分)
  10. CH5101 LICS//hdu5904 LICS