zoukankan      html  css  js  c++  java
  • 【洛谷 P4211】[LNOI2014]LCA(树链剖分,差分)

    题目链接

    看到题目肯定首先想到要求LCA(其实是我菜),可乍一看,n与q的规模为5W,
    求LCA的复杂度为(O(logN)),那么总时间复杂度为(O(nq log n))
    怎么搞呢?
    会树上差分的都知道,要对一条链进行操作,比如说链上的节点权值(+p),就要对两个端点分别(+p),然后对(LCA)及其父亲分别(-p)
    和这个思想差不多,设两个点为(u, v),那么求(dep(LCA(u,v)))只需要把((root,u))之前的路径所有点权值(+1),然后统计((root,v))路径上的权值和就行了,具体请自行脑补我觉得很好理解啊,这个过程显然可以用树剖来完成。
    然后就是差分,注意不是树上差分(sum_{i=l}^{r}LCA(i,z)=sum_{i=1}^{r}LCA(i,z)-sum_{i=1}^{l-1}LCA(i,z))
    考虑离线,设每个询问为((l,r,z)),定义一个结构体,包含(x)(端点),(k)(-1为左端点,1为右端点),(z)(要求LCA的点),(id)(是哪个询问),用2个空间分别存储左端点(l)和右端点(r),然后按端点编号排序。此时存放询问的数组应该是有(2q)个的。定义一个指针(cur),表示当前已经把前(cur)个点到根的路径(+1)了。枚举(i(1~2q)),如果是左端点,把(cur)更新到(x-1),((while(cur<x-1) Updata(1,++cur);)),把(ans[id])减去(z)到根的权值和,相当于(-sum_{i=1}^{l-1}LCA(i,z))。如果是右端点,同理,把(cur)更新到(x),然后(ans[id])加上...这题就愉快地切掉了。时间复杂度(O(n log^2 n))

    手写树剖一遍过,好激动

    #include <cstdio>
    #include <algorithm>
    using std::swap;
    using std::sort;
    #define left (now << 1)
    #define right (now << 1 | 1)
    const int MAXN = 50010;
    const int MOD = 201314;
    struct Edge{
        int next, to;
    }e[MAXN];
    int head[MAXN], num, n, q, a, b, c;
    int fa[MAXN], maxson[MAXN], size[MAXN], top[MAXN], dfsx[MAXN], ID, pos[MAXN], dep[MAXN], ans[MAXN];
    inline void Add(int from, int to){
        e[++num].to = to;
        e[num].next = head[from];
        head[from] = num;
    }
    int sum[MAXN << 2], lazy[MAXN << 2];
    inline void pushdown(int now, int len){
        if(lazy[now]){
          sum[left] += lazy[now] * (len - (len >> 1));
          sum[right] += lazy[now] * (len >> 1);
          lazy[left] += lazy[now];
          lazy[right] += lazy[now];
          lazy[now] = 0;
        }
    }
    inline void pushup(int now){
        sum[now] = sum[left] + sum[right];
    }
    /*void Build(int now, int l, int r){
        if(l != r){
          int mid = (l + r) >> 1;
          Build(left, l, mid);
          Build(right, mid + 1, r);
          pushup(now);
        }
        else sum[now] = a[l];
    }*/
    void Change(int now, int l, int r, int wl, int wr, int p){
        if(l > wr || r < wl) return ;
        if(l >= wl && r <= wr){
          sum[now] += p * (r - l + 1); lazy[now] += p; return ;
        }
        pushdown(now, r - l + 1);
        int mid = (l + r) >> 1;
        Change(left, l, mid, wl, wr, p);
        Change(right, mid + 1, r, wl, wr, p);
        pushup(now);
    }
    int Query(int now, int l, int r, int wl, int wr){
        if(l > wr || r < wl) return 0;
        if(l >= wl && r <= wr) return sum[now];
        pushdown(now, r - l + 1);
        int ans = 0;
        int mid = (l + r) >> 1;
        ans += Query(left, l, mid, wl, wr);
        ans += Query(right, mid + 1, r, wl, wr);
        return ans;
    }
    struct ASK{
        int x, z, k, id;
        bool operator < (const ASK &A) const{  //按照端点排序
            return x == A.x ? k < A.k : x < A.x;
        }
    }Ans[MAXN << 1];
    void dfs1(int u){
        dep[u] = dep[fa[u]] + 1;
        size[u] = 1;
        for(int i = head[u]; i; i = e[i].next){
           dfs1(e[i].to);
           size[u] += size[e[i].to];
           if(size[e[i].to] > size[maxson[u]])
             maxson[u] = e[i].to;
        }
    }
    void dfs2(int u, int t){
        dfsx[++ID] = u;
        top[u] = t;
        if(maxson[u]) dfs2(maxson[u], t);
        for(int i = head[u]; i; i = e[i].next){
           if(e[i].to != maxson[u])
             dfs2(e[i].to, e[i].to);
        }
    }
    void Updata(int u, int v, int p){
        int x = top[u], y = top[v];
        while(x != y){
          if(dep[x] > dep[y]) swap(x, y), swap(u, v);
          Change(1, 1, n, pos[y], pos[v], p);
          u = fa[x]; x = top[u];
          v = fa[y]; y = top[v];
        }
        if(dep[u] > dep[v]) swap(u, v);
        Change(1, 1, n, pos[u], pos[v], p);
    }
    int Solve(int u, int v){
        int ans = 0;
        int x = top[u], y = top[v];
        while(x != y){
          if(dep[x] > dep[y]) swap(x, y), swap(u, v);
          ans += Query(1, 1, n, pos[y], pos[v]);
          u = fa[x]; x = top[u];
          v = fa[y]; y = top[v];
        }
        if(dep[u] > dep[v]) swap(u, v);
        ans += Query(1, 1, n, pos[u], pos[v]);
        return ans;
    }
    int cur;
    int main(){
        scanf("%d%d", &n, &q);
        for(int i = 2; i <= n; ++i)
           scanf("%d", &fa[i]), Add(fa[i] += 1, i);
        for(int i = 1; i <= q; ++i){
           scanf("%d%d%d", &a, &b, &c);
           Ans[i].x = a + 1;
           Ans[i + q].x = b + 1;
           Ans[i].z = Ans[i + q].z = c + 1;
           Ans[i].k = -1;
           Ans[i + q].k = 1;
           Ans[i].id = Ans[i + q].id = i;
        }
        dfs1(1);
        dfs2(1, 1);
        for(int i = 1; i <= n; ++i)
           pos[dfsx[i]] = i;
        sort(Ans + 1, Ans + q * 2 + 1);
        for(int i = 1; i <= 2 * q; ++i){
           if(Ans[i].k < 0)  //是左端点
             while(cur < Ans[i].x - 1)
               Updata(1, ++cur, 1);
           else    //是右端点
             while(cur < Ans[i].x)
               Updata(1, ++cur, 1);
           ans[Ans[i].id] += Ans[i].k * Solve(1, Ans[i].z);
        }
        for(int i = 1; i <= q; ++i)
           printf("%d
    ", (ans[i] + MOD) % MOD);
        return 0;
    }
    
    
  • 相关阅读:
    使用Jackson对字典翻译
    通过代理的方式实现对httpClient的监控,超时回调
    mapstruct 1.4.2和lombok 1.18.16之后版本,报错和mapstruct生成空的实现
    jenkins + sonar 中文文件名报错解决
    linux 启动jar脚本
    easyUI关键(常见)组件详解
    shiro三连斩之概念
    SpringMVC请求参数总结
    springboot jsp,过滤器,拦截器
    CentOS添加使用
  • 原文地址:https://www.cnblogs.com/Qihoo360/p/9470790.html
Copyright © 2011-2022 走看看