zoukankan      html  css  js  c++  java
  • [AGC002D] Stamp Rally

    \(\text{Solution}\)

    很容易想到二分,然后 \(DFS\) 走所有能走的边,看是否大于等于 \(z\)
    这样就是 \(O(nm \log m)\)
    改进措施,思维不够,暴力来凑
    先改变下 \(check\)
    \(\le mid\) 的边加进图中,并查集维护连通性和 \(size\)
    直接判 \(x,y\)\(size\) 即可
    可二分,又多次,那就整体二分,可撤销并查集处理下即可
    \(O(m\log m\log n)\)

    其实可以更简单
    仍旧二分,考虑一个点到另一个点会走的边,显然是最小生成树上的边
    考虑 \(\text{Kruskal}\) 重构树的性质,不难想到倍增来 \(check\) 的做法
    \(O(m\log m\log n)\)

    \(\text{Code}\)

    #include <cstdio>
    #include <iostream>
    #define IN inline
    #define RE register
    using namespace std;
    
    const int N = 2e5 + 5, LG = 19;
    int n, m, q, f[N][LG], fa[N], val[N], h[N], tot, cnt[N];
    struct edge{int to, nxt;}e[N];
    IN void add(int x, int y){e[++tot] = edge{y, h[x]}, h[x] = tot;}
    
    IN void read(int &x)
    {
    	x = 0; char ch = getchar();
    	for(; !isdigit(ch); ch = getchar());
    	for(; isdigit(ch); x = (x<<3)+(x<<1)+(ch^48), ch = getchar());
    }
    int find(int x){return fa[x] == x ? x : (fa[x] = find(fa[x]));}
    void Dfs(int x)
    {
    	if (x <= n) cnt[x] = 1;
    	for(RE int i = 1; i < LG; i++)
    		if (f[x][i - 1]) f[x][i] = f[f[x][i - 1]][i - 1]; else break;
    	for(RE int i = h[x]; i; i = e[i].nxt)
    		f[e[i].to][0] = x, Dfs(e[i].to), cnt[x] += cnt[e[i].to];
    }
    IN void Kruskal()
    {
    	int SIZE = n;
    	for(RE int i = 1; i < n * 2; i++) fa[i] = i;
    	for(RE int i = 1, x, y, u, v; i <= m; i++)
    		read(x), read(y), u = find(x), v = find(y),
    		(u ^ v) && (val[++SIZE] = i, add(SIZE, u), add(SIZE, v), fa[u] = fa[v] = SIZE);
    	Dfs(SIZE);
    }
    IN int check(int mid, int x, int y, int z)
    {
    	for(RE int i = LG - 1; i >= 0; i--)
    		f[x][i] && (val[f[x][i]] <= mid) && (x = f[x][i]),
    		f[y][i] && (val[f[y][i]] <= mid) && (y = f[y][i]);
    	if (x == y)	return (cnt[x] >= z);
    	return (cnt[x] + cnt[y] >= z);
    }
    
    int main()
    {
    	read(n), read(m), Kruskal(), read(q);
    	for(RE int x, y, z, l, r, mid, ans; q; --q)
    	{
    		read(x), read(y), read(z), l = 1, r = m;
    		for(mid = l + r >> 1; l <= r; mid = l + r >> 1)
    			if (check(mid, x, y, z)) ans = mid, r = mid - 1; else l = mid + 1;
    		printf("%d\n", ans);
    	}
    }
    
  • 相关阅读:
    androidstudio配置模拟器路径
    Linux常见命令
    逆向助手使用
    Git服务器回滚到固定版本
    Git使用
    使用本地Gradle版本
    系统模拟器创建
    AndroidStudio设置代理
    关联、参数化、思考时间、检查点、事务的设置方式
    SQL:内连接、左外连接、右外连接、全连接、交叉连接区别
  • 原文地址:https://www.cnblogs.com/leiyuanze/p/15701935.html
Copyright © 2011-2022 走看看