BZOJ 3832 Poi2014 Rally 拓扑排序+堆

22 篇文章 0 订阅
10 篇文章 0 订阅

题目大意:给定一张拓扑图,要求删掉一个点使最长链最小,求删掉的点以及删掉后的最长链

这题真是神思路- -

首先我们建立源点和汇点 源点连向所有点 所有点连向汇点

那么图中最长链就变成了S到T的最长链

然后我们拓扑序DP求出S到每个点的最长链f[x]和每个点到T的最长链g[x]

我们令一条x->y的边的权值为f[x]+g[y]

那么这个图的最长链就转化成了所有边的边权的最大值

更进一步说 是这个图的一个割集中边权的最大值

那么我们的思路基本确定了:枚举删哪个点,用一些数据结构维护删点后割集中点权最大的边

但是这个怎么维护呢?

按照拓扑序删点就行了

令初始S集只有S 所有点和T都在T集

按照拓扑序依次将每个点从T集中删掉,加入S集

首先将这个点的所有入边从数据结构中删掉 此时割集的边权最大值就是删掉这个点的答案

然后再将这个点的所有出边加入数据结构中即可。

至于这个数据结构用什么。。。支持插入,删除,查询最大值,显然用堆就可以了。

跪Gromah,跪Claris


什么?你问我堆怎么删除?……


#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 1001001
using namespace std;
struct abcd{
	int to,next;
}table[M<<1];
int head[M],_head[M],tot;
int n,m,ans,ans_len=0x3f3f3f3f;
int degree[M],a[M],f[M],g[M];
void Add(int head[],int x,int y)
{
	table[++tot].to=y;
	table[tot].next=head[x];
	head[x]=tot;
}
namespace Heap{
	int heap[M],mark[M],top;
	void Insert(int x)
	{
		if(mark[x])
		{
			--mark[x];
			return ;
		}
		heap[++top]=x;
		int t=top;
		while(t>1)
		{
			if(heap[t]>heap[t>>1])
				swap(heap[t],heap[t>>1]),t>>=1;
			else
				break;
		}
	}
	void Delete(int x)
	{
		mark[x]++;
	}
	void Pop()
	{
		heap[1]=heap[top--];
		int t=2;
		while(t<=top)
		{
			if( t<top && heap[t+1]>heap[t] )
				t++;
			if(heap[t]>heap[t>>1])
				swap(heap[t],heap[t>>1]),t<<=1;
			else
				break;
		}
	}
	int Top()
	{
		while(mark[heap[1]])
			mark[heap[1]]--,Pop();
		return heap[1];
	}
}
void Topology_Sort()
{
	static int q[M],r,h;
	int i;
	for(i=1;i<=n;i++)
		if(!degree[i])
			q[++r]=i;
	while(r!=h)
	{
		int x=q[++h];
		for(i=head[x];i;i=table[i].next)
			if(!--degree[table[i].to])
				q[++r]=table[i].to;
	}
	memcpy(a,q,sizeof a);
}
int main()
{
	using namespace Heap;
	int i,j,x,y;
	cin>>n>>m;
	for(i=1;i<=m;i++)
	{
		scanf("%d%d",&x,&y);
		degree[y]++;
		Add(head,x,y);
		Add(_head,y,x);
	}
	Topology_Sort();
	for(j=1;j<=n;j++)
	{
		x=a[j];
		f[x]=max(f[x],1);
		for(i=head[x];i;i=table[i].next)
			f[table[i].to]=max(f[table[i].to],f[x]+1);
	}
	for(j=n;j;j--)
	{
		x=a[j];
		g[x]=max(g[x],1);
		for(i=head[x];i;i=table[i].next)
			g[x]=max(g[x],g[table[i].to]+1);
	}
	for(i=1;i<=n;i++)
		Insert(g[i]);
	Insert(0);
	for(j=1;j<=n;j++)
	{
		x=a[j];
		for(i=_head[x];i;i=table[i].next)
			Delete(f[table[i].to]+g[x]);
		Delete(g[x]);
		if(Top()<ans_len)
			ans_len=Top(),ans=x;
		for(i=head[x];i;i=table[i].next)
			Insert(f[x]+g[table[i].to]);
		Insert(f[x]);
	}
	cout<<ans<<' '<<ans_len-1<<endl;
	return 0;
}


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值