Time Limit: 5000/5000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others)
Total Submission(s): 892 Accepted Submission(s): 477
题目大意:
给你一张1e5个点的DAG图,所有出度为0的点为终点 ,1e5次询问,每次询问给你两个点,你可以删除图中任意一点,使得这两个点中至少一个点无法到达终点,输出满足条件的点数。
解法:
在不知道这题是灭绝树之前,想的是Tarjan缩点后求关键点,再新建一个源点和每个终点相连,最后查询两个点到源点路径上有多少关键点,然而敲完后才发现这样并不对T_T,不过已经很接近,就差灭绝树建树了哈哈。
从所有终点开始逆着拓扑,每个点的父亲结点就是他所有父亲的LCA(终点的父亲结点特判为源点),LCA的话动态维护一下,最后答案就是询问的两点和源点之间的路径交,也就是dis(U, T) + dis(V,T)- dis(lca(U,V),T)。
Accepted code
#pragma GCC optimize(3)
#include
#include
using namespace std;
#define sc scanf
#define ls rt << 1
#define rs ls | 1
#define Min(x, y) x = min(x, y)
#define Max(x, y) x = max(x, y)
#define ALL(x) (x).begin(),(x).end()
#define SZ(x) ((int)(x).size())
#define pir pair
#define MK(x, y) make_pair(x, y)
#define MEM(x, b) memset(x, b, sizeof(x))
#define MPY(x, b) memcpy(x, b, sizeof(x))
#define lowbit(x) ((x) & -(x))
#define P2(x) ((x) * (x))
typedef long long ll;
const int Mod = 1e9 + 7;
const int N = 1e5 + 100;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
inline ll dpow(ll a, ll b){ ll r = 1, t = a; while (b){ if (b & 1)r = (r*t) % Mod; b >>= 1; t = (t*t) % Mod; }return r; }
inline ll fpow(ll a, ll b){ ll r = 1, t = a; while (b){ if (b & 1)r = (r*t); b >>= 1; t = (t*t); }return r; }
vector G[N], r[N];
int f[N][25], dep[N];
int dis[N], in[N], n, m;
void Init() {
MEM(f, 0);
for (int i = 1; i <= n; i++) {
G[i].clear(), r[i].clear();
dep[i] = in[i] = 0;
}
}
int LCA(int x, int y) {
if (dep[x] > dep[y])
swap(x, y);
for (int i = 20; i >= 0; i--) {
if (dep[y] - (1 << i) >= dep[x])
y = f[y][i];
}
if (x == y)
return x;
for (int i = 20; i >= 0; i--) {
if (f[x][i] == f[y][i])
continue;
x = f[x][i], y = f[y][i];
}
return f[x][0];
}
void Topsort() {
queue q;
for (int i = 1; i <= n; i++) {
if (!in[i])
q.push(i);
}
while (!q.empty()) {
int u = q.front();
q.pop();
if (r[u].empty()) // 终点特判
f[u][0] = 0;
else {
int lca = r[u][0];
for (auto v : r[u]) // 所有父亲的LCA
lca = LCA(lca, v);
f[u][0] = lca;
}
dep[u] = dep[f[u][0]] + 1; // 动态维护
for (int i = 1; (1 << i) <= dep[u]; i++)
f[u][i] = f[f[u][i - 1]][i - 1];
for (auto v : G[u]) {
in[v]--;
if (!in[v])
q.push(v);
}
G[u].clear();
G[f[u][0]].push_back(u); // 存新图
}
}
int main()
{
int T; cin >> T;
while (T--) {
sc("%d %d", &n, &m);
Init();
for (int i = 0; i < m; i++) {
int u, v;
sc("%d %d", &u, &v);
r[u].push_back(v);
G[v].push_back(u);
in[u]++;
}
Topsort();
int q;
sc("%d", &q);
while (q--) {
int u, v;
sc("%d %d", &u, &v);
int lca = LCA(u, v);
printf("%d\n", dep[u] + dep[v] - dep[lca]);
}
}
return 0; // 改数组大小!!!用pair记得改宏定义!!!
}