• 树上差分


      一个写的很好的博客:https://blog.csdn.net/liuzibujian/article/details/81346595  

      差分真神奇...还可以跑到树上去。之前其实做过两个这种题,但是今天见到了一道神题“天天爱跑步”,发现树上差分远没有我想的那么简单。

     

      天天爱跑步:https://www.luogu.org/problemnew/show/P1600

      题意概述:给出一棵n个点的树以及树上的m条路径,每个点带有点权,求对于每个点,有多少条路径经过这个点时所走的长度恰好等于点权。

      这题的数据范围真有趣,专门用于写部分分。

      
      1 # include <cstdio>
      2 # include <iostream>
      3 # include <vector>
      4 # include <cstring>
      5 # define R register int
      6 
      7 using namespace std;
      8 
      9 const int maxn=300000;
     10 int n,m,x,y,h;
     11 int w[maxn],c[maxn];
     12 int s[maxn],t[maxn],vis[maxn],firs[maxn],dep[maxn],L[maxn];
     13 int F[maxn][20];
     14 vector <int> en[maxn];
     15 int num[maxn];
     16 struct edge
     17 {
     18     int too,nex;
     19 }g[maxn<<1];
     20 
     21 void add (int x,int y)
     22 {
     23     g[++h].too=y;
     24     g[h].nex=firs[x];
     25     firs[x]=h;
     26 }
     27 
     28 void st ()
     29 {
     30     for (int i=1;i<=m;++i)
     31         if(w[ s[i] ]==0) c[ s[i] ]++;
     32 }
     33 
     34 void dfs (int x)
     35 {
     36     int j;
     37     for (int i=firs[x];i;i=g[i].nex)
     38     {
     39         j=g[i].too;
     40         if(dep[j]) continue;
     41         dep[j]=dep[x]+1;
     42         F[j][0]=x;
     43         for (int i=1;i<=19;++i)
     44             F[j][i]=F[ F[j][i-1] ][i-1];
     45         dfs(j);
     46     }
     47 }
     48 
     49 int lca (int x,int y)
     50 {
     51     if(dep[x]>dep[y]) swap(x,y);
     52     for (int i=19;i>=0;--i)
     53         if(dep[x]<=dep[y]-(1<<i))
     54             y=F[y][i];
     55     if(x==y) return x;
     56     for (int i=19;i>=0;--i)
     57     {
     58         if(F[x][i]==F[y][i]) continue;
     59         x=F[x][i];
     60         y=F[y][i];
     61     }
     62     return F[x][0];
     63 }
     64 
     65 void link()
     66 {
     67     int siz;
     68     memset(vis,0,sizeof(vis));
     69     memset(num,0,sizeof(num));
     70     for (int i=1;i<=m;++i)
     71         if(s[i]<=t[i]) en[ t[i] ].push_back(s[i]),vis[ s[i] ]++;
     72     for (int i=1;i<=n;++i)
     73     {
     74         num[i]=vis[i];
     75         if(i-w[i]>=0) c[i]+=num[i-w[i]];
     76         siz=en[i].size();
     77         for (int j=0;j<siz;++j)
     78             num[ en[i][j] ]--;
     79     }
     80     memset(vis,0,sizeof(vis));
     81     memset(num,0,sizeof(num));
     82     for (int i=1;i<=m;++i)
     83         if(s[i]>t[i]) en[ t[i] ].push_back(s[i]),vis[ s[i] ]++;
     84     for (int i=n;i>=1;--i)
     85     {
     86         num[i]=vis[i];
     87         if(i+w[i]<=n) c[i]+=num[i+w[i]];
     88         siz=en[i].size();
     89         for (int j=0;j<siz;++j)
     90             num[ en[i][j] ]--;
     91     }
     92 }
     93 
     94 void bal()
     95 {
     96     for (int i=1;i<=m;++i)
     97         L[i]=lca(s[i],t[i]);
     98     for (int i=1;i<=m;++i)
     99     {
    100         int x=s[i],cnt=0;
    101         while (x!=L[i])
    102         {
    103             if(w[x]==cnt) c[x]++;
    104             x=F[x][0];
    105             cnt++;    
    106         }
    107         x=t[i],cnt=dep[ s[i] ]+dep[ t[i] ]-2*dep[ L[i] ];
    108         while (1)
    109         {
    110             if(w[x]==cnt) c[x]++;
    111             if(x==L[i]) break;
    112             x=F[x][0];
    113             cnt--;
    114         }
    115     }
    116 }
    117 
    118 void dfs1 (int x)
    119 {
    120     for (int i=firs[x];i;i=g[i].nex)
    121         if(dep[ g[i].too ]>dep[x])
    122         {
    123             dfs1(g[i].too);
    124             num[x]+=num[ g[i].too ];
    125         }
    126 }
    127 
    128 void s1()
    129 {
    130     for (int i=1;i<=m;++i)
    131         num[ t[i] ]++;
    132     dfs1(1);
    133     for (int i=1;i<=n;++i)
    134         if(dep[i]-1==w[i]) c[i]=num[i];
    135 }
    136 
    137 int main()
    138 {
    139     scanf("%d%d",&n,&m);
    140     for (R i=1;i<n;++i)
    141     {
    142         scanf("%d%d",&x,&y);
    143         add(x,y);
    144         add(y,x);
    145     }
    146     for (R i=1;i<=n;++i)
    147         scanf("%d",&w[i]);
    148     for (R i=1;i<=m;++i)
    149         scanf("%d%d",&s[i],&t[i]);
    150     dep[1]=1;
    151     dfs(1);
    152     if(n%10==1||n%10==2) st();
    153     else if(n%10==4) link();
    154     else if(n%10==3) bal();
    155     else if(n%10==5) s1();
    156     for (int i=1;i<=n;++i)
    157         printf("%d ",c[i]);
    158     return 0;
    159 }
    部分分集锦(60pts)

      只是终点为根的那一部分没有写,因为挺麻烦的,而且想到那个差不多就是正解了,但是不会实现于是就去看题解....

      还是简述一下部分分的做法:

      起点等于终点:只需要考虑对于每一个玩家的起点,观察员的$w$是否等于0即可; ---10pts √

      $w_j=0$:和上一个一样 ---10pts √

      NOIP送这么多分真的好吗...?

      $n<=1000$:暴力; ---5pts √

      树退化成一条链:我终于学会用vector均摊空间啦!分为从左往右和从右往左两种路径,先看从左往右的,在每个起点处打一个标记,再用vector存一下在每个点有哪些路径结束了,从左往右扫一遍。反着也是。---15pts √

      s都是1:起点都是一样,终点接着像上一个那样均摊空间,从根节点开始往下dfs,统计到每个点为止有多少路径还没有结束,因为起点统一的原因,每个观察员是否能观察到也是固定的,如果他能观察到人,只要是到这里还没有结束的玩家都能被看到,并不是很复杂的样子。 ---20pts

      t都是1:没写呀...但是现在想想也不是什么很难的东西,因为如果观察员能看到玩家,玩家一定是从观察员下面上来的,因为观察员的深度和$w$都是已知的,所以能看到的玩家的深度也是已知的,开一个桶统计目前深度为x的玩家有几个就好了,但是!即使是往下往上更新也会出问题,可能会更新到别的子树内的信息?只要这里能想到做法离满分就只差一点码力了,采用一种比较有趣的树上差分,在刚dfs到某个点时记录要用到的桶现在的值,等到dfs回来那个桶就会有新的值了,把这两个值相减得到的值就是它自己子树内的答案了!是不是很妙啊。 ---20pts √

      满分:如果刚刚那个差分思路能想到,满分自然也不难了,两个部分分提示的还不够明显吗?拆路径。把每条路径拆成$s->lca$和$lca->t$两条,第一种如果能被看到肯定也是从下面爬上来的,因为起点终点均不唯一,所以不能一开始全加上,可以开两个vector,不过也可以用正数表示这里有一个开始了,负数表示有一个这个数的相反数深度的路径结束,我觉得这样更舒服一点。第二种也是这样。但是起点,终点深度都不固定了,看起来很难算,让我们来“理性分析”一下。

      从观察员下面来的那些人,他们的深度就是$dep[x]+w[x]$,比较简单,对于从上面下来的人,他们走到观察员这里应该正好是第$w_i$个结点,也就是说:$dep[s]-dep[lca]+dep[x]-dep[lca]=w[x]$,这样移项一番就是一个定值,接着用差分桶维护。最后还有一个小细节,如果lca正好是一个满足条件的点,它就会被算两次,枚举每条路径的LCA把这种情况减掉。

      这真是个神题啊...看懂了之后觉得无比自然,甚至感觉就是一种暴力的优化,可是不看题解就想不到这种奇妙的做法呢。来,上代码。

      
      1 # include <cstdio>
      2 # include <iostream>
      3 # include <vector>
      4 # include <cstring>
      5 # define R register int
      6 
      7 using namespace std;
      8 
      9 const int maxn=3000000;
     10 int n,m,x,y,h;
     11 int w[maxn],c[maxn],s[maxn],t[maxn],vis[maxn],firs[maxn],dep[maxn],lca[maxn];
     12 int T[maxn<<1];
     13 int F[maxn][20];
     14 vector <int> v[maxn];
     15 int num[maxn];
     16 struct edge
     17 {
     18     int too,nex;
     19 }g[maxn<<1];
     20 
     21 void add (int x,int y)
     22 {
     23     g[++h].too=y;
     24     g[h].nex=firs[x];
     25     firs[x]=h;
     26 }
     27 
     28 void dfs (int x)
     29 {
     30     int j;
     31     for (int i=firs[x];i;i=g[i].nex)
     32     {
     33         j=g[i].too;
     34         if(dep[j]) continue;
     35         dep[j]=dep[x]+1;
     36         F[j][0]=x;
     37         for (int i=1;i<=19;++i)
     38             F[j][i]=F[ F[j][i-1] ][i-1];
     39         dfs(j);
     40     }
     41 }
     42 
     43 int Lca (int x,int y)
     44 {
     45     if(dep[x]>dep[y]) swap(x,y);
     46     for (int i=19;i>=0;--i)
     47         if(dep[x]<=dep[y]-(1<<i))
     48             y=F[y][i];
     49     if(x==y) return x;
     50     for (int i=19;i>=0;--i)
     51     {
     52         if(F[x][i]==F[y][i]) continue;
     53         x=F[x][i];
     54         y=F[y][i];
     55     }
     56     return F[x][0];
     57 }
     58 
     59 void dfss (int x)
     60 {
     61     int j,p1=T[dep[x]+w[x]],siz;
     62     for (R i=firs[x];i;i=g[i].nex)
     63     {
     64         j=g[i].too;
     65         if(dep[j]<dep[x]) continue;
     66         dfss(j);
     67     }
     68     siz=v[x].size();
     69     for (R i=0;i<siz;++i)
     70     {
     71         j=v[x][i];
     72         if(j>=0) T[j]++;
     73         else T[-j]--;    
     74     }
     75     c[x]+=T[dep[x]+w[x]]-p1;
     76 }
     77 
     78 void dfst (int x)
     79 {
     80     int j,p1=T[w[x]-dep[x]+n],siz;
     81     for (R i=firs[x];i;i=g[i].nex)
     82     {
     83         j=g[i].too;
     84         if(dep[j]<dep[x]) continue;
     85         dfst(j);
     86     }
     87     siz=v[x].size();
     88     for (R i=0;i<siz;++i)
     89     {
     90         j=v[x][i];
     91         if(j>=0) T[j]++;
     92         else T[-j]--;    
     93     }
     94     c[x]+=T[w[x]-dep[x]+n]-p1;
     95 }
     96 
     97 int main()
     98 {
     99     scanf("%d%d",&n,&m);
    100     for (R i=1;i<n;++i)
    101     {
    102         scanf("%d%d",&x,&y);
    103         add(x,y);
    104         add(y,x);
    105     }
    106     for (R i=1;i<=n;++i)
    107         scanf("%d",&w[i]);
    108     for (R i=1;i<=m;++i)
    109         scanf("%d%d",&s[i],&t[i]);
    110     dep[1]=1;
    111     dfs(1);
    112     for (R i=1;i<=m;++i)
    113         lca[i]=Lca(s[i],t[i]);
    114     
    115     for (R i=1;i<=m;++i)
    116         v[ s[i] ].push_back(dep[ s[i] ]),v[ F[ lca[i] ][0] ].push_back( -dep[ s[i] ]);
    117     dfss(1);
    118     for (R i=1;i<=n*2;++i) v[i].clear();
    119     
    120     for (R i=1;i<=m;++i)
    121         v[ t[i] ].push_back( -2*dep[ lca[i] ]+dep[ s[i] ]+n ),v[ F[ lca[i] ][0] ].push_back( -(-2*dep[ lca[i] ]+dep[ s[i] ]+n) );
    122     dfst(1);
    123     
    124     for (R i=1;i<=m;++i)
    125         if(dep[ s[i] ]-dep[ lca[i] ]==w[ lca[i] ]) c[ lca[i] ]--;
    126     for (int i=1;i<=n;++i)
    127         printf("%d ",c[i]);
    128     return 0;
    129 }
    天天爱跑步

      ---shzr

  • 相关阅读:
    linux curses函数库
    在Android library中不能使用switch-case语句访问资源ID的原因分析及解决方案
    Android Support ;v4、v7、v13的区别
    background-position
    java web 之 web.xml篇
    javaweb之Cookie篇
    Enumeration 接口
    Java Bad version number in .class file
    使用AppCan自带的升级功能实现移动端升级
    obj.offsetHeight与obj.style.height区别
  • 原文地址:https://www.cnblogs.com/shzr/p/9489318.html
Copyright © 2020-2023  润新知