一个非前缀和做法。
思路:首先,将图通过 (tarjan) 的无向图连通分量缩成一个无向无环图,即一棵树。那么,剩下的我们就是要判断一条路径上是否存在权值为 (1) 的边或点了。这里所谓的边和点都是指新图上的。那么,对于内部包含原图权值为 (1) 的边的边双连通,我们记一个数组 (val),代表其内部包含 (1)。
但是,注意,在连通与连通之间,可能仍然存在权值为 (1) 的边。所以,我们考虑边权下放点权,记录到一个 (sum) 数组中。
重点!
在查询的时候,(lca) 所代表的边不能被查询,但是其点权应该被查询,所以应该分开讨论。
上代码:
#include <bits/stdc++.h>
using namespace std;
#define N 1000010
template <class T>
inline void read(T& a){
T x = 0, s = 1;
char c = getchar();
while(!isdigit(c)){
if(c == '-') s = -1;
c = getchar();
}
while(isdigit(c)){
x = x * 10 + (c ^ '0');
c = getchar();
}
a = x * s;
return ;
}
struct tu{
struct node{
int u, v, w, next;
} t[N << 1];
int f[N];
int bian;
inline void add(int u, int v, int w){
t[++bian] = (node){u, v, w, f[u]}, f[u] = bian;
return ;
}
} T, G;
int n, m;
int val[N];
namespace suodian{ // 无向图的缩点
int dfn[N], low[N], id = 0;
int stac[N], top = 0;
int scc[N], cnt = 0;
void tarjan(int now, int fa){
dfn[now] = low[now] = ++id;
stac[++top] = now;
for(int i = G.f[now]; i; i = G.t[i].next){
int v = G.t[i].v;
if(!dfn[v]){
tarjan(v, now);
low[now] = min(low[now], low[v]);
}
else if(v != fa) low[now] = min(low[now], dfn[v]);
}
if(low[now] == dfn[now]){
int cur;
cnt++;
do{
cur = stac[top--];
scc[cur] = cnt;
}while(cur != now);
}
return ;
}
void work(){
for(int i = 1; i <= n; i++)
if(!dfn[i]) tarjan(i, 0); // 虽然题目中说了保证连通,但还是保险一点
for(int i = 1; i <= G.bian; i++){
int u = G.t[i].u, v = G.t[i].v;
if(scc[u] == scc[v]){
val[scc[u]] |= G.t[i].w; // 判断是否有 1
}
else if(scc[u] != scc[v])
T.add(scc[u], scc[v], G.t[i].w);
}
return ;
}
}
int fa[N], son[N], siz[N], top[N], deth[N];
int sum[N];
int dfn[N], rev[N], id = 0;
void dfs1(int now, int father){
sum[now] |= val[now]; // 全部累积到 sum 上
fa[now] = father;
siz[now] = 1;
deth[now] = deth[father] + 1;
for(int i = T.f[now]; i; i = T.t[i].next){
int v = T.t[i].v;
if(v != father){
dfs1(v, now);
sum[v] |= T.t[i].w; // 新权值累积下去
siz[now] += siz[v];
if(siz[v] > siz[son[now]])
son[now] = v;
}
}
return ;
}
void dfs2(int now, int tp){
top[now] = tp;
dfn[now] = ++id, rev[id] = now;
if(!son[now]) return ;
dfs2(son[now], tp);
for(int i = T.f[now]; i; i = T.t[i].next){
int v = T.t[i].v;
if(v != fa[now] && v != son[now])
dfs2(v, v);
}
return ;
}
struct Segment_tree{ // 路径求和
struct node{
int w;
} e[N << 2];
#define lson (o<<1)
#define rson (o<<1|1)
inline void pushup(int o){
e[o].w = e[lson].w + e[rson].w;
return ;
}
void build(int o, int l, int r){
if(l == r){
e[o].w = sum[rev[l]];
return ;
}
int mid = l + r >> 1;
build(lson, l, mid);
build(rson, mid + 1, r);
pushup(o);
return ;
}
int query(int o, int l, int r, int in, int end){
if(l > end || r < in) return 0;
if(l >= in && r <= end) return e[o].w;
int mid = l + r >> 1;
return query(lson, l, mid, in, end) + query(rson, mid + 1, r, in, end);
}
} tree;
int ask_he(int x, int y){ // 经典树剖对路径操作
int ans = 0;
while(top[x] != top[y]){
if(deth[top[x]] < deth[top[y]]) swap(x, y);
ans += tree.query(1, 1, n, dfn[top[x]], dfn[x]);
x = fa[top[x]];
}
if(deth[x] > deth[y]) swap(x, y);
ans += tree.query(1, 1, n, dfn[x] + 1, dfn[y]);
if(val[x]) ans++;
return ans;
}
int main(){
// freopen("hh.txt", "r", stdin);
read(n), read(m);
for(int i = 1; i <= m; i++){
int x, y, w;
read(x), read(y), read(w);
G.add(x, y, w); G.add(y, x, w);
}
suodian::work(); // 缩点
dfs1(1, 0);
dfs2(1, 0); // 树剖
tree.build(1, 1, n);
int q; read(q);
while(q--){
int s, ht;
read(s), read(ht);
s = suodian::scc[s], ht = suodian::scc[ht]; // 注意转换成新图
ask_he(s, ht) >= 1 ? puts("YES") : puts("NO");
}
return 0;
}