zoukankan      html  css  js  c++  java
  • 初涉倍增&&LCA【在更】

    一种特殊的枚举算法

    什么是倍增

    顾名思义,即每一次翻倍增加。那么,这样我们就有了一种$O(logn)$阶的方法处理枚举方面的问题了。

    参考:【白话系列】倍增算法

    一些题目

    【倍增】luoguP1613 跑路

    题目描述

    小A的工作不仅繁琐,更有苛刻的规定,要求小A每天早上在6:00之前到达公司,否则这个月工资清零。可是小A偏偏又有赖床的坏毛病。于是为了保住自己的工资,小A买了一个十分牛B的空间跑路器,每秒钟可以跑2^k千米(k是任意自然数)。当然,这个机器是用longint存的,所以总跑路长度不能超过maxlongint千米。小A的家到公司的路可以看做一个有向图,小A家为点1,公司为点n,每条边长度均为一千米。小A想每天能醒地尽量晚,所以让你帮他算算,他最少需要几秒才能到公司。数据保证1到n至少有一条路径。

    数据范围

    50%的数据满足最优解路径长度<=1000;

    100%的数据满足n<=50,m<=10000,最优解路径长度<=maxlongint。


    题目分析

    首先,显然这题是不能求最短路的跑路次数的。那么我们用bool类型的$f[i][j][t]$表示$i$到$j$存在长度为$2^t$的路径。

    这样做可以使得$dis[i][j]$表示的是从$i$到$j$的最短跑路次数,因而最后用floyd求解最短路就好了。

    参考:题解 P1613 【跑路】

     1 #include<bits/stdc++.h>
     2 const int maxn = 61;
     3 
     4 bool f[maxn][maxn][71];
     5 int dis[maxn][maxn];
     6 int n,m;
     7 
     8 int main()
     9 {
    10     scanf("%d%d",&n,&m);
    11     memset(dis, 0x3f3f3f3f, sizeof dis);
    12     for (int i=1; i<=m; i++)
    13     {
    14         int x,y;
    15         scanf("%d%d",&x,&y);
    16         f[x][y][0] = 1;
    17         dis[x][y] = 1;
    18     }
    19     for (int t=1; t<=64; t++)
    20         for (int k=1; k<=n; k++)
    21             for (int i=1; i<=n; i++)
    22                 for (int j=1; j<=n; j++)
    23                     if (f[i][k][t-1]&&f[k][j][t-1]){
    24                         f[i][j][t] = 1;
    25                         dis[i][j] = 1;
    26                     }
    27     for (int k=1; k<=n; k++)
    28         for (int i=1; i<=n; i++)
    29             for (int j=1; j<=n; j++)
    30                 if (dis[i][k]+dis[k][j] < dis[i][j])
    31                     dis[i][j] = dis[i][k]+dis[k][j];
    32     printf("%d
    ",dis[1][n]);
    33     return 0;
    34 }

    【LCA】luoguP3379 【模板】最近公共祖先(LCA)

    题目描述

    如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先。

    输入输出格式

    输入格式:

    第一行包含三个正整数N、M、S,分别表示树的结点个数、询问的个数和树根结点的序号。

    接下来N-1行每行包含两个正整数x、y,表示x结点和y结点之间有一条直接连接的边(数据保证可以构成树)。

    接下来M行每行包含两个正整数a、b,表示询问a结点和b结点的最近公共祖先。

    输出格式:

    输出包含M行,每行包含一个正整数,依次为每一个询问的结果。

    说明

    时空限制:1000ms,128M

    数据规模:

    对于30%的数据:N<=10,M<=10

    对于70%的数据:N<=10000,M<=10000

    对于100%的数据:N<=500000,M<=500000


    题目分析

    呃这是一个裸的LCA题,那么就讲讲模拟法和倍增法吧(RMQ暂且不提)

    模拟法

    为了查询LCA(x,y),我们从x开始一直向上染色。再从y开始一直向上查询,最先查询到的染色过的点就是LCA(x,y)。

    有一个技巧,把染色的vis开成int数组,这样每一次就不用memset了(很慢的)

     1 #include<bits/stdc++.h>
     2 const int maxn = 500003;
     3 
     4 std::vector<int> f[maxn];
     5 int fa[maxn];
     6 int n,m,s;
     7 int vis[maxn];
     8 
     9 int read()
    10 {
    11     char ch = getchar();
    12     int num = 0;
    13     for (; !isdigit(ch); ch = getchar());
    14     for (; isdigit(ch); ch = getchar())
    15         num = (num<<1)+(num<<3)+ch-48;
    16     return num;
    17 }
    18 void buildStructure(int now)
    19 {
    20     for (unsigned int i=0; i<f[now].size(); i++)
    21     if (!fa[f[now][i]])
    22     {
    23         fa[f[now][i]] = now;
    24         buildStructure(f[now][i]);
    25     }
    26 }
    27 int main()
    28 {
    29     n = read(), m = read(), s = read();
    30     for (int i=1; i<n; i++)
    31     {
    32         int x = read(), y = read();
    33         f[x].push_back(y), f[y].push_back(x);
    34     }
    35     fa[s] = s;
    36     buildStructure(s);
    37     fa[s] = 0;
    38     for (int i=1; i<=m; i++)
    39     {
    40         int x = read(), y = read();
    41         for (; x; x = fa[x]) vis[x] = i;
    42         for (; y; y = fa[y])
    43             if (vis[y] == i)
    44                 break;
    45         printf("%d
    ",y);
    46     }
    47     return 0;
    48 }

    当然以上做法是过不了这题的

    倍增法

    倍增的话,算是一种对于模拟法的优化吧。

    我们每一次不止跳一步,而是以$2^i$步数向上跳。

    那么就需要O(nlogn)预处理一些东西,查询则是O(logn)的。

    参考:

    1.【白话系列】最近公共祖先

    2.LCA(离线Tarjan算法,在线倍增法)详解

    3.LCA详解

    4.题解 P3379 【【模板】最近公共祖先(LCA)】

     1 #include<bits/stdc++.h>
     2 const int maxn = 500003;
     3 const int logMaxn = 23;
     4 
     5 int n,m,s;
     6 int deep[maxn],p[maxn][logMaxn];
     7 std::vector<int> f[maxn];
     8 
     9 inline int read()
    10 {
    11     char ch = getchar();
    12     int num = 0;
    13     for (; !isdigit(ch); ch = getchar());
    14     for (; isdigit(ch); ch = getchar())
    15         num = (num<<1)+(num<<3)+ch-48;
    16     return num;
    17 }
    18 void dfs(int now, int fa)          //递归预处理
    19 {
    20     deep[now] = deep[fa]+1;
    21     p[now][0] = fa;
    22     for (int i=1; (1<<i)<=deep[now]; i++)
    23         p[now][i] = p[p[now][i-1]][i-1];
    24     for (unsigned int i=0; i<f[now].size(); i++)
    25         if (f[now][i]!=fa)
    26             dfs(f[now][i], now);
    27 }
    28 int lca(int x, int y)            //倍增LCA
    29 {
    30     if (deep[x] > deep[y]) std::swap(x, y);
    31     for (int i=logMaxn-1; i>=0; i--)
    32         if (deep[x]<=deep[y]-(1<<i))
    33             y = p[y][i];
    34     if (x==y) return x;
    35     for (int i=logMaxn-1; i>=0; i--)
    36         if (p[x][i]==p[y][i])
    37             continue;
    38         else x = p[x][i], y = p[y][i];
    39     return p[x][0];
    40 }
    41 int main()
    42 {
    43     n = read(), m = read(), s = read();
    44     register int i,x,y;
    45     for (i=1; i<n; i++)
    46     {
    47         x = read(), y = read();
    48         f[x].push_back(y), f[y].push_back(x);
    49     }
    50     dfs(s, 0);
    51     for (i=1; i<=m; i++)
    52     {
    53         x = read(), y = read();
    54         printf("%d
    ",lca(x, y));
    55     }
    56     return 0;
    57 }

    【LCA】luoguP3398 仓鼠找sugar

    题目描述

    小仓鼠的和他的基(mei)友(zi)sugar住在地下洞穴中,每个节点的编号为1~n。地下洞穴是一个树形结构。这一天小仓鼠打算从从他的卧室(a)到餐厅(b),而他的基友同时要从他的卧室(c)到图书馆(d)。他们都会走最短路径。现在小仓鼠希望知道,有没有可能在某个地方,可以碰到他的基友?

    小仓鼠那么弱,还要天天被zzq大爷虐,请你快来救救他吧!

    输入输出格式

    输入格式:

    第一行两个正整数n和q,表示这棵树节点的个数和询问的个数。

    接下来n-1行,每行两个正整数u和v,表示节点u到节点v之间有一条边。

    接下来q行,每行四个正整数a、b、c和d,表示节点编号,也就是一次询问,其意义如上。

    输出格式:

    对于每个询问,如果有公共点,输出大写字母“Y”;否则输出“N”。

    说明

    __本题时限1s,内存限制128M,因新评测机速度较为接近NOIP评测机速度,请注意常数问题带来的影响。__

    20%的数据 n<=200,q<=200

    40%的数据 n<=2000,q<=2000

    70%的数据 n<=50000,q<=50000

    100%的数据 n<=100000,q<=100000


    题目分析

    题目大意就是判断树上(a,b)与(c,d)两条路径之间有没有交点。

    看上去很唬人的样子对吧……

    【待更】其实我也觉得很唬人

  • 相关阅读:
    c读取文本文档
    java类中定义接口
    android selector
    android listview
    android继承Dialog实现自定义对话框
    移植net-snmp到开发板(mini210)
    [BZOJ1901]Zju2112 Dynamic Rankings
    [BZOJ3524][Poi2014]Couriers
    [codeforces722D]Generating Sets
    [codeforces722C]Destroying Array
  • 原文地址:https://www.cnblogs.com/antiquality/p/8994781.html
Copyright © 2011-2022 走看看