图论神仙题

一些性质

  1. 将 DAG 按照拓扑序依次放在数轴上,每条边都是从左边的一个点连向右边的一个点。
  2. 删去图中的环后剩下的森林由独立的树和连在环上的数组成。

P3573 [POI2014] RAJ-Rally

题目
考虑算出删去每个点时的最长路径。
因为起点和终点是任意的,所以考虑新建超级源汇点,并连 S → i S\rightarrow i Si i → T i\rightarrow T iT 的边。
然后我们拓扑排序,然后按照拓扑序将每个点依次放在数轴上,那么图中的每条边都是从数轴上左边的一个点 u u u 连向右边的一个点 v v v。假设 u u u 的拓扑序是 x x x v v v 的拓扑序是 y y y,那么拓扑序处于 [ x + 1 , y − 1 ] [x+1,y-1] [x+1,y1] 范围内的点删除后不会影响到这条边。所以只需再记 f i f_i fi 表示 S S S i i i 的最长路径, g i g_i gi 表示 i i i T T T 的最长路径,假设最终删去第 i i i 个点时的最值是 a n s i ans_i ansi,那么就可以用 ( f u + g v + 1 ) − 2 (f_u+g_v+1)-2 (fu+gv+1)2 更新 a n s i ( i ∈ [ x + 1 , y − 1 ] ) ans_i(i\in[x+1,y-1]) ansi(i[x+1,y1])

CF512D Fox And Travelling

题目

可以注意到拓扑排序和图的连通性还是很有关系的。

首先可以注意到,如果一个点在环里,那么它一定不会被选择。所以先做一遍拓扑排序把环去掉。
然后图只剩下一些独立的树和连在环上的树。
先考虑连在环上的树。
f [ u ] [ i ] f[u][i] f[u][i] 表示在以 u u u 为根的子树中,选择了 i i i 个点。
转移是 f [ u ] [ i + j ] = ∑ f [ u ] [ i ] × f [ v ] [ j ] × ( i + j i ) f[u][i+j]=\sum f[u][i]\times f[v][j]\times\binom{i+j}{i} f[u][i+j]=f[u][i]×f[v][j]×(ii+j)
对于独立的树,根是任意的,所以对于每一个点都当做根做一遍上面的 dp。然后求和后对 r e s i res_i resi 除以 s z − i sz-i szi s z sz sz 表示这棵树的大小。
dp 部分稍微有点细节

CF527E Data Center Drama

题目

CF576D Flights for Regular Customers

题目
挺厉害的一道题,质量挺高的。
首先将边按照 d i d_i di 从小到大排序,然后在只考虑目前加入的边形成的边集中考虑更新答案。
从小到大解锁每条边,用矩阵维护走恰好 d i d_i di 步可以走到的点,然后用已解锁的边跑 floyd ,然后用前半部分和后半部分拼一块求出答案。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<map>
#include<queue>
#include<bitset>
#define int long long
using namespace std;
const int N=155;
int n,m,ans=1ll<<60;
int dis[N][N];
inline int read()
{
	int s=0,t=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')t=-1;ch=getchar();}
	while(ch>='0'&&ch<='9') s=(s<<1)+(s<<3)+(ch^48),ch=getchar();
	return s*t;
}
struct node
{
	int u,v,tim;
}e[N];
inline bool cmp(node p,node q){return p.tim<q.tim;}
struct matrix
{
	bitset<N> a[N];
}A;
inline void init(matrix &res){for(int i=1;i<=n;++i) res.a[i].reset(),res.a[i][i]=1;}
inline matrix operator * (const matrix &p,const matrix &q)
{
	matrix res;
	for(int i=1;i<=n;++i)
		for(int j=1;j<=n;++j)
			if(p.a[i][j]) res.a[i]|=q.a[j]; // res.a[i][k]=p.a[i][j]|q.a[j][k]
	return res;
}
inline matrix ksm(matrix a,int b)
{
	matrix res;init(res);
	while(b)
	{
		if(b&1) res=res*a;
		a=a*a;
		b>>=1;
	}
	return res;
}
signed main()
{
	n=read();
	m=read();
	for(int i=1;i<=m;++i)
	{
		e[i].u=read();
		e[i].v=read();
		e[i].tim=read();
	}
	sort(e+1,e+m+1,cmp);
	for(int i=1;i<=n;++i) A.a[i][i]=1;
	for(int i=1;i<=m;++i)
	{
		matrix B;
		for(int j=1;j<=i-1;++j) B.a[e[j].u][e[j].v]=1;
		A=A*ksm(B,e[i].tim-e[i-1].tim);
		for(int j=1;j<=n;++j)
			for(int k=1;k<=n;++k)
				dis[j][k]=1ll<<60;
		for(int j=1;j<=n;++j) dis[j][j]=0;
		for(int j=1;j<=i;++j) dis[e[j].u][e[j].v]=1;
		for(int c=1;c<=n;++c)
			for(int a=1;a<=n;++a)
				for(int b=1;b<=n;++b)
				{
					if(dis[a][c]==1ll<<60||dis[c][b]==1ll<<60) continue;
					dis[a][b]=min(dis[a][b],dis[a][c]+dis[c][b]);
				}
		for(int j=1;j<=n;++j)
			if(A.a[1][j]) ans=min(ans,e[i].tim+dis[j][n]);
	}
	if(ans==1ll<<60) puts("Impossible");
	else printf("%lld",ans);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值