zoukankan      html  css  js  c++  java
  • Luogu:P1600 天天爱跑步

    来一发清新的80行 树剖 $LCA$ + 树上差分 题解。

    -----from Judge


    本题题意大概是给出一棵 n 个节点的树以及 m 条有向路径,

    并且每个点 i 都有一个权值 $w[i]$,如果某条路径包含了 i 号节点,并且 i 号节点是该路径上的第 $w[i]$ 个节点的话就会对答案产生贡献。

    考虑暴力做法

    我们可以十分理所当然的想到一个暴力: u 和 v 向上跑,沿路判断条件并累加,直到跑到 $LCA$ 。

    但是这个暴力的时间复杂度太高了: $O (n + m )$ 。于是我们考虑别的方法。

    首先我们看 $n$ 和 $m$ 都是 $3e5$ 的数据范围,那么我们对于 $m$ 条路径的处理当然不能太大, 那么我们就考虑:用 **起点** 和 **终点** 来累加答案。

    考虑路径的拆分

    那么如何累加? 我们先考虑:每个节点要对答案产生贡献则必然出现在一条路径$(u->v)$ 上,又因为 $u$ 先会跑到 $LCA(u,v)$ ,然后再跑到 $v$。

    即:$u$ -> $LCA(u,v)$ -> $v$

    那么我们可以把路径拆成两条,分别处理 $u$ 到 $lca$ 路径上点的贡献,以及 $lca$ 到 $v$ 路径上点的贡献

    考虑第一条路径上的点

    我们可以推导出,在 $dep[i] + w[i] = dep[u]$ 的情况先, i 号节点会对答案产生贡献。

    即:在 i 的子树内,若有 $dep[u] = dep[i] + w[i]$ (至于 i 不在 $u -> LCA$ 的路径上的情况我们可以用差分思想解决)

    那么我们只需要判断 i 的子树内 有无 $dep[u]$ 满足该式即可,对此我们可以开一个计数数组 $cnt$ 。

    然后对于一个点我们可以记录下当前点出发的节点数,每次深搜到该点就对计数数组累加,

    同时我们扫描当前节点作为哪些子节点的 $LCA$ 出现过,将这些子节点的 $dep$ 减掉就可以达到差分的效果了。

    考虑第二条路径上的点

    类似的,我们可以推导出关于 u,v 和 i 的等式:
    $dep[i] - w[i] = dep[v] - dis(u,v)$

    那么这里 dep 减掉 w[i] 后可能是负数,对此我们将左右式同时加上 n 即可(题目条件:$w[i]<=n$)。

    那么这样我们就要判断 i 的子树内是否有 $dep[v] - dis(u,v) = dep[i] - w[i]$ 即可。

    然后同上操作。

    遍历处理

    那么我们只需要对树进行两次遍历就可以处理出以上信息了。

    考虑重复贡献的删除

    我们可以观察到,如果某个节点 $i$ 就是 $u$ 、$v$ 的 $LCA$
    那么该节点的贡献是会被累加两次的。对此我们如何消除多余贡献?这里有两种方法:

    暴力删除

    在程序最后暴力枚举 m 条路径 的 LCA 并判断其是否在该路径上,在的话就减贡献。

    修改遍历树的方式

    其实我们完全可以在第二次遍历树的时候先对以 $i$ 为 $LCA$ 的路径上的终点信息先删除,然后再累加答案,相当于我们在做第二条路径的时候忽略掉 $LCA$ 这个节点。那么对此的操作也很简单,程序执行顺序换一下就好了。

     1 //by Judge
     2 #include<iostream>
     3 #include<vector>
     4 #include<cstdio>
     5 #define ll long long
     6 using namespace std;
     7 const int M=3e5+11;
     8 const int inf=1e9+7;
     9 #ifndef Judge
    10 #define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
    11 #endif
    12 char buf[1<<21],*p1=buf,*p2=buf;
    13 inline int read(){
    14     int x=0,f=1; char c=getchar();
    15     for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
    16     for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f;
    17 } char sr[1<<21],z[20];int C=-1,Z;
    18 inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;}
    19 inline void print(int x){
    20     if(C>1<<20)Ot();if(x<0)sr[++C]=45,x=-x;
    21     while(z[++Z]=x%10+48,x/=10);
    22     while(sr[++C]=z[Z],--Z);sr[++C]=' ';
    23 } int n,m,pat,mx;
    24 int w[M],s[M],cnt1[M],cnt2[M<<1],ans[M],siz[M],dep[M],son[M],f[M],top[M],head[M];
    25 vector<int> q1[M],q2[M],q3[M];
    26 struct operation{ int u,v,lca,dis; }a[M];
    27 struct Edge{ int to,next; Edge(int to,int next):to(to),next(next){} Edge(){} }e[M<<1];
    28 inline void add(int u,int v){
    29     e[++pat]=Edge(v,head[u]),head[u]=pat;
    30     e[++pat]=Edge(u,head[v]),head[v]=pat;
    31 }
    32 #define v e[i].to
    33 void dfs(int u,int fa){
    34     siz[u]=1,dep[u]=dep[f[u]=fa]+1;
    35     for(int i=head[u];i;i=e[i].next) if(v^fa){
    36         dfs(v,u),siz[u]+=siz[v];
    37         if(siz[v]>siz[son[u]]) son[u]=v;
    38     }
    39 } void dfs(int u){
    40     if(!top[u]) top[u]=u; if(!son[u]) return ;
    41     top[son[u]]=top[u], dfs(son[u]);
    42     for(int i=head[u];i;i=e[i].next)
    43         if(v^f[u] && v^son[u]) dfs(v);
    44 } void dfs1(int u){
    45     int now=w[u]+dep[u],cun; if(now<=mx) cun=cnt1[now];
    46     for(int i=head[u];i;i=e[i].next) if(v^f[u]) dfs1(v);
    47     cnt1[dep[u]]+=s[u]; if(now<=mx) ans[u]=cnt1[now]-cun;
    48     for(int i=0;i<q1[u].size();++i) --cnt1[dep[q1[u][i]]];
    49 } void dfs2(int u){
    50     int now=dep[u]-w[u]+n,cum=cnt2[now];
    51     for(int i=head[u];i;i=e[i].next) if(v^f[u]) dfs2(v);
    52     for(int i=0;i<q2[u].size();++i) ++cnt2[q2[u][i]+n];
    53     for(int i=0;i<q3[u].size();++i) --cnt2[q3[u][i]+n];
    54     ans[u]+=cnt2[now]-cum;
    55 }
    56 #undef v
    57 inline int LCA(int u,int v){
    58     while(top[u]^top[v]){
    59         dep[top[u]]>dep[top[v]]?u=f[top[u]]:v=f[top[v]];
    60     } return dep[u]<dep[v]?u:v;
    61 }
    62 int main(){
    63     n=read(),m=read();
    64     for(int i=1,u,v;i<n;++i)
    65         u=read(),v=read(),add(u,v);
    66     for(int i=1;i<=n;++i) w[i]=read();
    67     for(int i=1;i<=m;++i) a[i].u=read(),a[i].v=read();
    68     dep[1]=1,dfs(1,0),dfs(1); for(int i=1;i<=n;++i) mx=max(mx,dep[i]);
    69     for(int i=1;i<=m;++i){
    70         a[i].lca=LCA(a[i].u,a[i].v),++s[a[i].u];
    71         a[i].dis=dep[a[i].u]+dep[a[i].v]-dep[a[i].lca]*2;
    72         q1[a[i].lca].push_back(a[i].u);
    73     } dfs1(1);
    74     for(int i=1;i<=m;++i){
    75         q2[a[i].v].push_back(dep[a[i].v]-a[i].dis);
    76         q3[a[i].lca].push_back(dep[a[i].v]-a[i].dis);
    77     } dfs2(1);
    78     for(int i=1;i<=n;++i) print(ans[i]); return Ot(),putchar('
    '),0;
    79 }
  • 相关阅读:
    SQL语句实例学习汇总
    sql语句一些实用技巧for oracle
    无限级递归生成HTML示例及ListBox,DropDownList等无限树
    另类Sql语句直接导出表数据到Execl
    powerdesigner中sql脚本小写转大写,去双引号
    C#中利用jQuery获取Json值示例,Ajax方式。
    利用jquery解决下拉菜单被Div遮挡问题
    获取Textarea 元素当前的光标位置及document.selection.createRange()资料
    oracle中一些常用函数
    IE6 动态创建 iframe 无法显示的 bug,万恶的IE6
  • 原文地址:https://www.cnblogs.com/Judge/p/9678173.html
Copyright © 2011-2022 走看看