P2416 【泡芙】

一个非前缀和做法。

思路:首先,将图通过 \(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; 
}
posted @ 2020-12-04 08:16  雪之下,树之旁  阅读(146)  评论(0编辑  收藏  举报