[樹上差分]天天愛跑步

一道糾結了很久的題

重點不知道的是差分是對於每個桶而言的

樹上差分能解決一部分樹上統計問題

u表示起點,v表示終點

對於一條u到v的路徑,先討論LCA!=u&&LCA!=v的情況:

分爲u到LCA的路徑和LCA到v的路徑

對於u到LCA的路徑上的點x,當deep[u]-deep[x]=w[x]時,即w[x]+deep[x]=deep[u]時,這條路徑對點x有貢獻;

觀察發現w[x]+deep[x]是定值,所以統計經過x的路徑中,deep[u]=w[x]+deep[x]的路徑條數。

對於LCA到v的路徑上的點x,當deep[u]-2*deep[LCA]+deep[x]=w[x]時,即w[x]-deep[x]=deep[u]-2*deep[lca]時,這條路徑對點x有貢獻;

觀察發現w[x]-deep[x]是定值,所以統計經過x的路徑中,deep[u]-2*deep[lca]=w[x]-deep[x]的路徑條數;

接下來就是統計路徑條數了,用到樹上差分

我們統計的起點(終點)一定在點x子樹內,所以統計x子樹內有多少起點(終點)的值等於所需值

即統計有多少個在點x子樹內的起點的deep[u]的值與deep[x]+w[x]相同

有多少終點的deep[u]-2*deep[lca]與w[x]-deep[x]相同

對於一個值,再u、v上加一個表示這個值+1的標記

考慮到x子樹內的路徑不一定經過x,所以在father[LCA]上加一個標記表示這個值-1

標記用動態數組儲存  然後一遍dfs用兩個桶分別統計,統計時值統一加上n,因爲可能出現負數

記錄下dfs到父親節點時自己(也就是父親的兒子)所需值的個數,然後統計完子樹的值之後再做差計算自己

對於LCA==u||LCA==v的情況歸於以上兩類計算,特殊處理一下

另外,對於分裂成兩條鏈LCA可能會被統計兩遍,最後特殊判斷一下,如果被統計了兩遍就減去一遍,

複雜度:LCA O(mlogn)dfs統計 O(n)

 

#include<bits/stdc++.h>
using namespace std;
const int N=350000;
int Max,x,y,z,len,n,m,tot,w[N],tong1[4*N],tong2[4*N],ans[N],to[N*2],head[N],next1[N*2],d[N],f[N][30];
struct p{
    int type,zhi,qz;
};
int read(){
    char ch=getchar();int x=0,op=1;
    for (;!isdigit(ch);ch=getchar()) if (ch=='-') op=-1;
    for (;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+ch-'0';
    return x*op;
}
vector <p> t[4*N];
void add(int x,int y)
{  next1[++tot]=head[x];
   head[x]=tot;
   to[tot]=y;
}
void dfs(int x)
{  for (int i=head[x];i;i=next1[i])
    {  int v=to[i]; 
       if (!d[v]) 
       { d[v]=d[x]+1; 
         f[v][0]=x; 
         dfs(v);}
    }
}
int  lca(int x,int y)
{    if (d[x]<d[y])  swap(x,y);
   int tmp=d[x]-d[y];
   for (int i=25;i>=0;i--)  
   if ((tmp>>i)&1) 
    {x=f[x][i];}
   if (x==y) { return x;}
   else {for (int i=25;i>=0;i--)  
         if (f[x][i]!=f[y][i]) { x=f[x][i]; y=f[y][i];}
         x=f[x][0]; return x;
        }
}
void solve(int x)
{   int tmp=tong1[d[x]+w[x]]+tong2[d[x]-w[x]+Max];//tong1記錄向上走的鏈 tong2記錄向下走的鏈 
    for (int i=0;i<t[x].size();i++)
    { if (t[x][i].type==1)  {tong1[t[x][i].zhi]+=t[x][i].qz;}
                       else {tong2[t[x][i].zhi]+=t[x][i].qz;}}
   for (int i=head[x];i;i=next1[i])
   { if (d[to[i]]>d[x]) { solve(to[i]);} }
   ans[x]=tong1[d[x]+w[x]]+tong2[d[x]-w[x]+Max]-tmp;
}
int main()
{  n=read(); m=read();  Max=3*n;
   for (int i=1;i<=n-1;i++)
   {  x=read(); y=read();  add(x,y); add(y,x);}
     d[1]=1;dfs(1);
  	 for (int i=1;i<=n;i++)
       for (int j=1;(1<<j)<d[i];j++)
       {  f[i][j]=f[f[i][j-1]][j-1];}
   for (int i=1;i<=n;i++)   w[i]=read();
   for (int i=1;i<=m;i++)
   {  x=read(); y=read(); z=lca(x,y);
       len=d[x]+d[y]-2*d[z];
       t[x].push_back(p{1,d[x],1});
       t[y].push_back(p{2,d[y]-len+Max,1});
       t[z].push_back(p{1,d[x],-1});
       t[f[z][0]].push_back(p{2,d[y]-len+Max,-1});//對桶進行樹上差分
   }
   solve(1);
   for (int i=1;i<=n-1;i++)  cout<<ans[i]<<' ';
   cout<<ans[n]<<endl;	 	 
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章