zoukankan      html  css  js  c++  java
  • LCA(包含RMQ)

    今天看了RMQ问题
    ST的实质是动归
    于是我来回顾一下LCA(的各种写法)
    因为每次考试发现自己连LCA都写不好
    费时

    First of all, RMQ板子:

    [一维]

    #include<bits/stdc++.h>
    
    using namespace std;
    
    const int N = 50010;
    
    int n, q, a, b;
    int c[N];
    int dpmn[N][20], dpmx[N][20];
    
    template <typename T>
    T read(){
    	T N(0), F(1);
    	char C = getchar();
    	for(; !isdigit(C); C = getchar()) if(C == '-') F = -1;
    	for(; isdigit(C); C = getchar()) N = N*10 + C-48;
    	return N*F;
    }
    
    void rmqmx(){
    	for(int i = 1; i <= n; i++) dpmx[i][0] = c[i];
    
    	for(int j = 1; (1<<j) <= n; j++)
    		for(int i = 1; i+(1<<j)-1<=n; i++)
    			dpmx[i][j] = max(dpmx[i][j-1], dpmx[i+(1<<(j-1))][j-1]);	
    }
    
    void rmqmn(){
    	for(int i = 1; i <= n; i++) dpmn[i][0] = c[i];
    
    	for(int j = 1; (1<<j) <= n; j++)
    		for(int i = 1; i+(1<<j)-1 <= n; i++)
    			dpmn[i][j] = min(dpmn[i][j-1], dpmn[i+(1<<(j-1))][j-1]);
    }
    
    int rmx(int l, int r){
    	int k = (int)(log(r-l+1.0)/log(2.0));
    	return max(dpmx[l][k], dpmx[r-(1<<k)+1][k]);
    }
    
    int rmn(int l, int r){
    	int k = (int)(log(r-l+1.0)/log(2.0));
    	return min(dpmn[l][k], dpmn[r-(1<<k)+1][k]);
    }
    
    int main(){
    	freopen("rmq.in", "r", stdin);
    	freopen("rmq.out","w",stdout);
    
    	n = read<int>(); q = read<int>();
    	for(int i = 1; i <= n; i++) c[i] = read<int>();
    	
    	rmqmx();
    	rmqmn();
    
    	for(int i = 1; i <= q; i++){
    		a = read<int>(); b = read<int>();
    		printf("%d
    ", rmx(a, b) - rmn(a, b));
    	}
    
    	return 0;
    }
    

    [二维-n*m(可能超空间)]

    #include<bits/stdc++.h>
    
    using namespace std;
    
    const int N = 260;
    
    template <typename T>
    T read(){
    	T n(0), f(1);
    	char ch = getchar();
    	for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = -1;
    	for(; isdigit(ch); ch = getchar()) n = n*10 + ch-48;
    	return n*f;
    }
    
    int n, k, b;
    int a[N][N];
    int rmx[N][N][8][8], rmn[N][N][8][8];
    
    void init(){
    	for(int i = 1; i <= n; i++)
    		for(int j = 1; j <= n; j++) rmx[i][j][0][0] = rmn[i][j][0][0] = a[i][j];
    
    	for(int p = 0; (1<<p) <= n; p++){
    	for(int q = 0; (1<<q) <= n; q++){
    		if(p+q){
    		for(int i = 1; i+(1<<p)-1 <= n; i++){
    		for(int j = 1; j+(1<<q)-1 <= n; j++){
    			if(p){
    				rmx[i][j][p][q] = max(rmx[i][j][p-1][q], rmx[i+(1<<(p-1))][j][p-1][q]);
    				rmn[i][j][p][q] = min(rmn[i][j][p-1][q], rmx[i+(1<<(p-1))][j][p-1][q]);
    			}
    			else{
    				rmx[i][j][p][q] = max(rmx[i][j][p][q-1], rmx[i][j+(1<<(q-1))][p][q-1]);
    			        rmn[i][j][p][q] = min(rmn[i][j][p][q-1], rmn[i][j+(1<<(q-1))][p][q-1]);
    			}
    		}
    		}
                    }
    	}
    	}
    }
    
    int rmqmax(int x1, int y1, int x2, int y2){
    	int k1 = 0;
    	while(1<<(k1+1) <= x2-x1+1) k1++;
    	int k2 = 0;
    	while(1<<(k2+1) <= y2-y1+1) k2++;
    	x2 = x2 - (1<<k1) + 1;
    	y2 = y2 - (1<<k2) + 1;
    	return max(max(rmx[x1][y1][k1][k2], rmx[x1][y2][k1][k2]), max(rmx[x2][y1][k1][k2], rmx[x2][y2][k1][k2]));
    }
    
    int rmqmin(int x1, int y1, int x2, int y2){
    	int k1 = 0;
    	while(1<<(k1+1) <= x2-x1+1) k1++;
    	int k2 = 0;
    	while(1<<(k2+1) <= y2-y1+1) k2++;
    	x2 = x2 - (1<<k1) + 1;
    	y2 = y2 - (1<<k2) + 1;
    	return min(min(rmn[x1][y1][k1][k2], rmn[x1][y2][k1][k2]), min(rmn[x2][y1][k1][k2], rmn[x2][y2][k1][k2]));
    }
    
    int main(){
    	freopen("p2019.in", "r", stdin);
    	freopen("p2019.out","w",stdout);
    
    	n = read<int>(); b = read<int>(); k = read<int>();
    	for(int i = 1; i <= n; i++){
    		for(int j = 1; j <= n; j++){
    			a[i][j] = read<int>();
    		}
    	}
    
    	init();
    	
    	int x, y;
    	while(k--){
    		x = read<int>(); y = read<int>();
    		int ans = rmqmax(x, y, x+b-1, y+b-1) - rmqmin(x, y, x+b-1, y+b-1);
    		printf("%d
    ", ans);
    	}
    
    	return 0;
    }
    

    [二维-n*n(空间上好一点点?)]

    #include<stdio.h>
    #include<string.h>
    #include<iostream>
    #include<math.h>
    using namespace std;
    int maxn[255][255][15];
    int minn[255][255][15];
    int n,b,q;
    void ST()
    {
        int len=floor(log10(double(n))/log10(double(2)));
        for(int k=1;k<=n;k++)
        {
            for(int j=1;j<=len;j++)
            {
                for(int i=1;i<=(n+1)-(1<<j);i++)
                {
                    maxn[k][i][j]=max(maxn[k][i][j-1],maxn[k][i+(1<<(j-1))][j-1]);
                    minn[k][i][j]=min(minn[k][i][j-1],minn[k][i+(1<<(j-1))][j-1]);
                }
            }
        }
    }
    int main()
    {
        while(~scanf("%d%d%d",&n,&b,&q))
        {
            memset(maxn,0,sizeof(maxn));
            memset(minn,0,sizeof(minn));
            for(int i=1;i<=n;i++)
            {
                for(int j=1;j<=n;j++)
                {
                    int tmp;
                    scanf("%d",&tmp);
                    maxn[i][j][0]=tmp;
                    minn[i][j][0]=tmp;
                }
            }
            ST();
            while(q--)
            {
                int x,y;
                int ma=-0x3f3f3f3f;
                int mi=0x3f3f3f3f;
                scanf("%d%d",&x,&y);
                int len=floor(log10(double(b))/log10(double(2)));
                for(int i=x;i<x+b;i++)
                {
                    ma=max(max(maxn[i][y][len],maxn[i][(y+b-1)-(1<<len)+1][len]),ma);
                    mi=min(min(minn[i][y][len],minn[i][(y+b-1)-(1<<len)+1][len]),mi);
                }
                //printf("%d %d
    ",ma,mi);
                printf("%d
    ",ma-mi);
            }
        }
    }
    
    

    在线:DFS+ST(思想是:将树看成一个无向图,u和v的公共祖先一定在u与v之间的最短路径上)

    在线:DFS+倍增
    跳到同一层,然后一起跳。

    poj1330
    题意:求一对点的LCA,无边权。

    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<vector>
    
    using namespace std;
    
    const int N = 10010;
    
    template <typename T>
    T read(){
    	T n(0), f(1);
    	char ch = getchar();
    	for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = -1;
    	for(; isdigit(ch); ch = getchar()) n = n*10 + ch-48;
    	return n*f;
    }
    
    int t, n, e, qx, qy, rt;
    int f[N][30];
    int to[N<<1], nxt[N<<1];
    int dep[N], Begin[N], flag[N];
    
    void init(){
    	e = 0;
    	memset(to, 0, sizeof(to));
    	memset(nxt, 0, sizeof(nxt));
    	memset(Begin, 0, sizeof(Begin));
    	for(int i = 1; i <= n; ++i){
    		dep[i] = flag[i] = 0;
    	}
    }
    
    void add(int x, int y){
    	to[++e] = y; nxt[e] = Begin[x]; Begin[x] = e;
    }
    
    void dfs(int u, int f = 0){
    	for(int i = Begin[u]; i; i = nxt[i]){
    		int v = to[i];
    		if(v == f) continue;
    		dep[v] = dep[u]+1;
    		dfs(v, u);
    	}
    }
    
    int lca(int u, int v){
    	if(dep[u] < dep[v]) swap(u, v);
    	int dis = dep[u]-dep[v];
    	for(int i = 0; i <= 29; ++i){
    		if((dis & (1<<i))) u = f[u][i];
    	}
    	if(u == v) return u;
    	for(int i = 29; i >= 0; --i){
    		if(f[u][i] != f[v][i] && f[u][i]){
    			u = f[u][i]; v = f[v][i];
    		}
    	}
    	return f[u][0];
    }
    
    int main(){
    	t = read<int>();
    	while(t--){
    		n = read<int>();
    		init();
    		for(int i = 1; i < n; ++i){
    			int x, y;
    			x = read<int>();
    			y = read<int>();
    			add(x, y);
    			f[y][0] = x;
    			flag[y]++;
    		}
    		for(int j = 1; j <= 29; ++j)
    			for(int i = 1; i <= n; ++i)
    				f[i][j] = f[f[i][j-1]][j-1];
    		
    		for(int i = 1; i <= n; ++i) if(!flag[i]) dfs(i);
    
    		qx = read<int>(); qy = read<int>();
    		printf("%d
    ", lca(qx, qy));
    	}
    
    	return 0;
    }
    

    离线:Tarjan
    Here

    poj1330
    题意:求一对点的LCA,无边权。
    P.S.顺便知道如何处理可能为森林的情况。

    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<vector>
    
    using namespace std;
    
    const int N = 10010;
    
    template <typename T>
    T read(){
    	T n(0), f(1);
    	char ch = getchar();
    	for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = -1;
    	for(; isdigit(ch); ch = getchar()) n = n*10 + ch-48;
    	return n*f;
    }
    
    int t, n, e, qx, qy, ans;
    int to[N<<1], nxt[N<<1];
    int begin[N], vis[N], deg[N], fa[N];
    vector<int> p[N];
    
    void init(){
    	ans = e = 0;
    	memset(vis, 0, sizeof(vis));
    	memset(deg, 0, sizeof(deg));
    	memset(to, 0, sizeof(to));
    	memset(nxt, 0, sizeof(nxt));
    	memset(begin, 0, sizeof(begin));
    	for(int i = 1; i <= n; ++i){
    		fa[i] = i;
    		p[i].clear();
    	}
    }
    
    void add(int x, int y){
    	to[++e] = y; nxt[e] = begin[x]; begin[x] = e;
    }
    
    int find(int x){
    	return x == fa[x] ? x : fa[x] = find(fa[x]);
    }
    
    void tarjan(int u, int f = 0){
    	for(int i = begin[u]; i; i = nxt[i]){
    		int v = to[i];
    		if(v == f) continue;
    		tarjan(v, u);
    		fa[v] = u;
    	}
    	vis[u] = 1;
    	for(int i = 0; i < p[u].size(); ++i){
    		int v = p[u][i];
    		if(vis[v]){
    			ans = find(v);
    			return;
    		}
    	}
    }
    
    int main(){
    	t = read<int>();
    	while(t--){
    		n = read<int>();
    		init();
    		for(int i = 1; i < n; ++i){
    			int x, y;
    			x = read<int>();
    			y = read<int>();
    			add(x, y);
    			deg[y]++;
    		}
    		qx = read<int>(); qy = read<int>();
    		p[qx].push_back(qy);
    		p[qy].push_back(qx);
    		for(int i = 1; i <= n; ++i)
    			if(!deg[i]) tarjan(i);
    		printf("%d
    ", ans);
    	}
    
    	return 0;
    }
    

    codevs2370
    题意:给定有边权的树,m组询问给出点对,求点对之间的最短路。
    P.S.
    1、记dep[i]为i到根节点的距离,x、y间的最短路为dep[x]+dep[y]-2*dep[lca(x, y)].
    2、离线会导致得到答案的顺序与询问顺序不同,用链式前向星记录,即可按顺序输出。

    #include<bits/stdc++.h>
    
    using namespace std;
    
    const int N = 50010;
    const int M = 75010;
    
    template <typename T>
    T read(){
    	T n(0), f(1);
    	char ch = getchar();
    	for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = -1;
    	for(; isdigit(ch); ch = getchar()) n = n*10 + ch-48;
    	return n*f;
    }
    
    int n, m, e, e_;
    int to[N<<1], nxt[N<<1], w[N<<1];
    int to_[M<<1], nxt_[M<<1], w_[M<<1];
    int begin[N], fa[N], begin_[M], dep[N], vis[N]; 
    
    void init(){
    	for(int i = 1; i <= n; ++i) fa[i] = i;
    }
    
    void add(int x, int y, int z){
    	to[++e] = y; nxt[e] = begin[x]; w[e] = z; begin[x] = e;
    }
    
    void add_(int x, int y){
    	to_[++e_] = y; nxt_[e_] = begin_[x]; begin_[x] = e_;
    }
    
    int find(int x){
    	return x == fa[x] ? x : fa[x] = find(fa[x]);
    }
    
    void dfs(int u, int f = 0){
    	for(int i = begin[u]; i; i = nxt[i]){
    		int v = to[i];
    		if(v == f) continue;
    		dep[v] = dep[u] + w[i];
    		dfs(v, u);
    	}
    }
    
    void tarjan(int u, int f = 0){
    	for(int i = begin[u]; i; i = nxt[i]){
    		int v = to[i];
    		if(v == f) continue;
    		tarjan(v, u);
    		fa[v] = u;
    	}
    	vis[u] = 1;
    	for(int i = begin_[u]; i; i = nxt_[i]){
    		int v = to_[i];
    		if(vis[v]){
    			w_[i] = dep[u] + dep[v] - 2*dep[find(v)];
    			if(i&1) w_[i+1] = dep[u] + dep[v] - 2*dep[find(v)];
    			else w_[i-1] = dep[u] + dep[v] - 2*dep[find(v)];
    		}
    	}
    }
    
    int main(){
    	n = read<int>();
    	init();
    	for(int i = 1; i < n; ++i){
    		int x, y, z;
    		x = read<int>()+1;
    		y = read<int>()+1;
    		z = read<int>();
    		add(x, y, z);
    		add(y, x, z);
    	}
    	dfs(1);
    	//for(int i = 1; i <= n; ++i) printf("%d ", dep[i]);
    	m = read<int>();
    	for(int i = 1; i <= m; ++i){
    		int x, y;
    		x = read<int>()+1;
    		y = read<int>()+1;
    		add_(x, y);
    		add_(y, x);
    	}
    	tarjan(1);
    	
    	for(int i = 1; i <= e_; i+=2){
    		printf("%d
    ", w_[i]);
    	}
    
    	return 0;
    }
    
  • 相关阅读:
    条款1:理解模板类型推导
    非受限联合体
    整型
    vector作为函数返回类型
    SQL Server数据库空间管理 (1)
    1085 PAT单位排行 (25 分
    1084 外观数列 (20 分)
    1083 是否存在相等的差 (20 分)
    1082 射击比赛 (20 分)
    1081 检查密码 (15 分)
  • 原文地址:https://www.cnblogs.com/hanser/p/7688398.html
Copyright © 2011-2022 走看看