一些性质
- 将 DAG 按照拓扑序依次放在数轴上,每条边都是从左边的一个点连向右边的一个点。
- 删去图中的环后剩下的森林由独立的树和连在环上的数组成。
P3573 [POI2014] RAJ-Rally
题目
考虑算出删去每个点时的最长路径。
因为起点和终点是任意的,所以考虑新建超级源汇点,并连
S
→
i
S\rightarrow i
S→i 和
i
→
T
i\rightarrow T
i→T 的边。
然后我们拓扑排序,然后按照拓扑序将每个点依次放在数轴上,那么图中的每条边都是从数轴上左边的一个点
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,y−1] 范围内的点删除后不会影响到这条边。所以只需再记
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,y−1])。
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
sz−i,
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;
}