Loading

题目归档 #9

目录

  • P6378 [PA2010] Riddle
  • [JOISC 2014 Day3] 电压
  • P4197 Peaks

P6378 [PA2010] Riddle

显然是个 2-SAT 问题,对于相邻的点很容易设置约束条件。然而对于一个部分最多一个关键点这个条件,如果暴力建 2-SAT 的约束边是 \(O(n^2)\) 的,显然不能过。

所以肯定要优化建图了。一开始想的是直接莽线段树优化建图,结果倒数第三个测试点一直 TLE。

看了题解,发现是 前缀和优化建图。考虑增加一倍的点 \(pre\),若其中 \(pre_i=1\) 代表在 \(j \in [1,i]\) 中有 \(a_j=1\)

然后就可以 \(O(n)\) 地建图了。

  • 代码:

    int n, m, k, U[Maxn], V[Maxn], siz[Maxn], change[Maxn];
    
    struct Edge {
    	int next, to;
    }
    edge[Maxn * 10];
    int head[Maxn * 4], edge_num; /* a: 0~2n pre: 2n~4n */
    
    inline void add_edge(int from, int to) {
    	edge[++edge_num].next = head[from];
    	edge[edge_num].to = to;
    	head[from] = edge_num;
    }
    
    int dfn[Maxn * 4], low[Maxn * 4], stack[Maxn * 4], scc[Maxn * 4], top, cnt, scc_num;
    bool ins[Maxn * 4];
    void tarjan(int u) {
    	dfn[u] = low[u] = ++cnt;
    	stack[++top] = u; ins[u] = 1;
    	for(int i = head[u]; i; i = edge[i].next) {
    		int v = edge[i].to;
    		if(!dfn[v]) {
    			// cout << u << " " << v << endl;
    			tarjan(v);
    			low[u] = min(low[u], low[v]);
    		}
    		else if(ins[v]) low[u] = min(low[u], dfn[v]);
    	}
    	if(low[u] == dfn[u]) {
    		++scc_num;
    		while(stack[top] != u) {
    			ins[stack[top]] = 0;
    			scc[stack[top--]] = scc_num;
    		}
    		ins[stack[top]] = 0;
    		scc[stack[top--]] = scc_num;
    	}
    }
    
    int main() {
    	n = read(); m = read(); k = read();
    	for(int i = 1; i <= m; ++i) U[i] = read(), V[i] = read();
    	int cnt = 0;
    	for(int i = 1; i <= k; ++i) {
    		siz[i] = read();
    		for(int j = 1; j <= siz[i]; ++j) change[read()] = ++cnt;
        }
    	for(int i = 1; i <= m; ++i) U[i] = change[U[i]], V[i] = change[V[i]];
    	cnt = 0;
    	for(int i = 1; i <= k; ++i) {
    		for(int j = 1; j <= siz[i]; ++j) {
    			++cnt;
    			add_edge(cnt + n, cnt + 3 * n), add_edge(cnt + 2 * n, cnt);
    			if(j > 1) add_edge(cnt - 1 + 3 * n, cnt + 3 * n), add_edge(cnt + 2 * n, cnt - 1 + 2 * n);
    			if(j > 1) add_edge(cnt - 1 + 3 * n, cnt), add_edge(cnt + n, cnt - 1 + 2 * n);
    		}
    	}
    	for(int i = 1; i <= m; ++i) add_edge(U[i], V[i] + n), add_edge(V[i], U[i] + n);
    	for(int i = 1; i <= n * 4; ++i) if(!dfn[i]) tarjan(i);
    	bool flag = 1;
    	for(int i = 1; i <= n; ++i)
    		if(scc[i] == scc[i + n]) {
    			flag = 0;
    			break;
    		}
    	if(flag) cout << "TAK" << endl;
    	else cout << "NIE" << endl;
    	return 0;
    }
    

[JOISC 2014 Day3] 电压

显然,选择的这条边必须满足以下两个条件:

  • 在所有奇环上
  • 不在任何一个偶环上

因此构建生成树。考虑所有的 返祖边 所构成的环的大小(可以通过深度算出),看是奇数还是偶数。

然后树上差分,打标记即可。

需要注意的是,返祖边能够被选,当且仅当图中仅存在 \(1\) 个奇环,这时答案加一。

  • 代码:

    int vis[Maxn], depth[Maxn], odd[Maxn], even[Maxn], tot;
    void dfs(int u, int inedge) {
        vis[u] = 1;
        for(int i = head[u]; i; i = edge[i].next) {
            int v = edge[i].to;
            if(i == (inedge ^ 1)) continue;
            if(!vis[v]) depth[v] = depth[u] + 1, dfs(v, i);
            else if(depth[v] < depth[u]) {
                int len = depth[u] - depth[v] + 1;
                if(len & 1) ++odd[u], --odd[v], ++tot;
                else ++even[u], --even[v];
            }
        }
    }
    
    int os[Maxn], es[Maxn], ans, root;
    void dfs2(int u) {
        vis[u] = 1; os[u] = odd[u]; es[u] = even[u];
        for(int i = head[u]; i; i = edge[i].next) {
            int v = edge[i].to;
            if(!vis[v]) dfs2(v), os[u] += os[v], es[u] += es[v];
        }
        if(u != root && os[u] == tot && !es[u]) ++ans;
    }
    
    int main() {
        n = read(); m = read();
        int u, v;
        for(int i = 1; i <= m; ++i) {
            u = read(); v = read();
            add_edge(u, v);
            add_edge(v, u);
        }
        for(int i = 1; i <= n; ++i) if(!vis[i]) depth[i] = 1, dfs(i, 0);
        memset(vis, 0, sizeof(vis));
        for(int i = 1; i <= n; ++i) if(!vis[i]) root = i, dfs2(i);
        if(tot == 1) ++ans;
        cout << ans << endl;
        return 0;
    }
    

P4197 Peaks

看到经过不大于某一值的边,考虑构建 kruskal 重构树。

重构树建好之后,对于每次询问,从点 \(v\) 跳倍增跳到恰好小于等于目标值的点,它的子树中的点可以互相通达。

然后用主席树维护子树第 \(k\) 大即可。(主席树很久没写的我特别难受。。。)

  • 代码:

    int n, m, q, T;
    int tot, all, fa[Maxn * 2], val[Maxn * 2], siz[Maxn * 2], id[Maxn * 2], root[Maxn * 2], raw[Maxn * 2];
    
    struct Hi {
    	int h, id, newh;
    }
    hi[Maxn * 2];
    
    struct Node {
    	int u, v, w;
    }
    node[Maxm];
    
    struct Edge {
    	int next, to;
    }
    edge[Maxn * 2];
    int head[Maxn * 2], edge_num;
    
    void add_edge(int from, int to) {
    	edge[++edge_num].next = head[from];
    	edge[edge_num].to = to;
    	head[from] = edge_num;
    }
    
    int cmp1(Hi fir, Hi sec) {return fir.h < sec.h;}
    int cmp2(Node fir, Node sec) {return fir.w < sec.w;}
    int cmp3(Hi fir, Hi sec) {return fir.id < sec.id;}
    
    int find(int x) {
    	if(fa[x] == x) return x;
    	else return fa[x] = find(fa[x]);
    }
    
    queue <int> Q; int f[Maxn * 2][Max];
    void bfs(int S) {
    	Q.push(S);
    	while(Q.size()) {
    		int u = Q.front(); Q.pop();
    		for(int i = head[u]; i; i = edge[i].next) {
    			int v = edge[i].to;
    			f[v][0] = u;
    			for(int j = 1; j < Max; ++j) {
    				f[v][j] = f[f[v][j - 1]][j - 1];
    				if(!f[v][j]) break;
    			}
    			Q.push(v);
    		}
    	}
    }
    
    struct Tree {
    	int ls, rs;
    	int sum;
    }
    tree[Maxn * 80];
    int counter = 0;
    
    int insert(int pre, int l, int r, int x, int val) {
    	int p = ++counter;
    	tree[p] = tree[pre];
    	if(l == r) {
    		tree[p].sum += val;
    		return p;
    	}
    	int mid = (l + r) >> 1;
    	if(x <= mid) tree[p].ls = insert(tree[p].ls, l, mid, x, val);
    	else tree[p].rs = insert(tree[p].rs, mid + 1, r, x, val);
    	tree[p].sum = tree[tree[p].ls].sum + tree[tree[p].rs].sum;
    	return p;
    }
    
    int query(int p, int q, int l, int r, int rk) {
    	//cout << "debug " << l << " " << r << " " << rk << " " << tree[p].sum << endl;
    	if(l == r) {
    		// cout << "debug " << tree[q].sum << " " << rk << " " << l << endl;
    		if(tree[q].sum - tree[p].sum >= rk) return l;
    		else return -1;
    	}
    	int diff = tree[tree[q].rs].sum - tree[tree[p].rs].sum;
    	int mid = (l + r) >> 1;
    	if(rk <= diff) return query(tree[p].rs, tree[q].rs, mid + 1, r, rk);
    	else return query(tree[p].ls, tree[q].ls, l, mid, rk - diff);
    }
    
    int build(int l, int r) {
    	int p = ++counter;
    	if(l == r) return p;
    	int mid = (l + r) >> 1;
    	tree[counter].ls = build(l, mid);
    	tree[counter].rs = build(mid + 1, r);
    	return p;
    }
    
    int cnt = 0;
    void dfs(int u) {
    	siz[u] = 1; id[u] = ++cnt;
    	if(hi[u].newh) root[cnt] = insert(root[cnt - 1], 1, all, hi[u].newh, 1);
    	else root[cnt] = root[cnt - 1];
    	// cout << "dfs " << u << " " << hi[u].newh << endl;
    	for(int i = head[u]; i; i = edge[i].next) {
    		int v = edge[i].to;
    		dfs(v);
    		siz[u] += siz[v];
    	}
    }
    
    void lsh() {
    	sort(hi + 1, hi + n + 1, cmp1);
    	hi[0].h = -1e9;
    	for(int i = 1; i <= n; ++i) {
    		if(hi[i].h == hi[i - 1].h) hi[i].newh = hi[i - 1].newh;
    		else hi[i].newh = hi[i - 1].newh + 1;
    		raw[hi[i].newh] = hi[i].h;
    	}
    	all = hi[n].newh;
    	sort(hi + 1, hi + n + 1, cmp3);
    }
    
    int solve(int v, int x, int k) {
    	for(int i = Max - 1; i >= 0; --i) {
    		if(!f[v][i]) continue;
    		if(val[f[v][i]] <= x) v = f[v][i];
    	}
    	// cout << "debug " << v << endl;
    	int ans = query(root[id[v]], root[id[v] + siz[v] - 1], 1, all, k);
    	if(ans == -1) return -1;
    	else return raw[ans];
    }
    
    int main() {
    	n = read(); m = read(); q = read(); tot = n;
    	for(int i = 1; i <= n; ++i) hi[i].id = i, hi[i].h = read();
    	lsh();
    	for(int i = 1; i <= m; ++i) {
    		node[i].u = read();
    		node[i].v = read();
    		node[i].w = read();
    	}
    	sort(node + 1, node + m + 1, cmp2);
    	for(int i = 1; i <= n * 2; ++i) fa[i] = i;
    	for(int i = 1; i <= m; ++i) {
    		int ancu = find(node[i].u), ancv = find(node[i].v);
    		if(ancu != ancv) {
    			val[++tot] = node[i].w;
    			add_edge(tot, ancu);
    			add_edge(tot, ancv);
    			fa[ancu] = fa[ancv] = tot;
    		}
    	}
    	bfs(tot);
    	root[0] = build(1, all);
    	dfs(tot);
    	int v, x, k, ans = 0;
    	for(int i = 1; i <= q; ++i) {
    		v = read(); x = read(); k = read();
    		// if(T) v = v ^ ans, x = x ^ ans, k = k ^ ans;
    		ans = solve(v, x, k);
    		printf("%d\n", ans);
    		if(ans == -1) ans = 0;
    	}
    	return 0;
    }
    
posted @ 2020-08-21 22:53  Sqrtyz  阅读(84)  评论(0)    收藏  举报