zoukankan      html  css  js  c++  java
  • 树上差分

      一个写的很好的博客: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

  • 相关阅读:
    JavaScript实现类的private、protected、public、static以及继承
    OSS网页上传和断点续传(STSToken篇)
    OSS网页上传和断点续传(OSS配置篇)
    Linq sum()时遇到NULL
    SQLSERVER事务日志已满 the transaction log for database 'xx' is full
    笔记本高分辨软件兼容问题,字体太小或模糊
    H5上传图片之canvas
    An error occurred while updating the entries. See the inner exception for details.
    无限级结构SQL查询所有的下级和所有的上级
    SQLserver 进程被死锁问题解决
  • 原文地址:https://www.cnblogs.com/shzr/p/9489318.html
Copyright © 2011-2022 走看看