一類子樹問題的總結

GreenDuck發表於2019-03-28

以下問題均允許離線,根節點都為1。

prob1 : 一棵有根樹,要求線性時間求出任意子樹的權值和。

prob2 : 一顆有根樹,要求O(nlogn)求出與u距離不超過x且在u子樹中的節點的權值和。

prob3 : 一顆有根樹,要求O(nlogn)求出與u距離不超過x且在u子樹中的不同顏色種類個數,顏色數不超過1E6。

prob4 : 一顆有根數,要求O(nlogn+大常數)求出與u距離不超過x且在u子樹中的權值的lcm取模,節點的值不超過1E6,質因子不超過1E4。TIPS:取模後傳統公式顯然不成立。

(未填完)


 

Prob 1

這個問題很簡單吧,找個dfs序做個字首和就可以了。

但是dfs序卻是很有用的。

沒有程式碼。


 

Prob 2

有了限制,但我們注意到這個問題只是詢問某個節點往下深度不超過x的權值和。

沿用prob1的思想,我們仍然想用字首和,而一個節點的貢獻與他的深度有關。

因此我們以深度為下標,按照dfs序依次加入可持久化線段樹中,就能計算答案了。

實現太簡單,沒有程式碼。


 

Prob 3

樹的問題太難了,我們先想鏈上問題怎麼做。

現在問題的關鍵在於,會有重複的計數,因此我們要消除這個影響。

將詢問離線下來,按照詢問右端點排序,依次加入每一個點。若該點與前面有重複的,在前面的位置上減去1的貢獻。

這樣就轉換為單點修改,區間查詢,樹狀陣列即可。

 

樹上也同樣,先求出dfs序,將詢問按deppos+x排序,再依次加入每一個點。若這個點與前面的有重複,要在所有與它重複的點lca離他距離最近處減去1的貢獻。

 

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 const int maxn=1E6+5;
  4 const int inf=INT_MAX;
  5 int head[maxn*2],size,dfn[maxn],ti,back[maxn],fa[maxn][21],last[maxn],c[maxn],n,m,dep[maxn],ans[maxn],x,y,maxD;
  6 struct edge{int to,next;}E[maxn*2];
  7 struct BIT
  8 {
  9     int t[maxn];
 10     int lowbit(int x){return x&-x;}
 11     void add(int x,int y){while(x<=n){t[x]+=y;x+=lowbit(x);}}
 12     int ask(int x){int ans=0;while(x){ans+=t[x];x-=lowbit(x);}return ans;}
 13 }T;
 14 struct query{int pos,d,num;}Q[maxn];
 15 vector<int>bel[maxn];
 16 set<int>pre[maxn];
 17 bool cmp(query x,query y){return dep[x.pos]+x.d<dep[y.pos]+y.d;}
 18 void add(int u,int v)
 19 {
 20     E[++size].to=v;
 21     E[size].next=head[u];
 22     head[u]=size;
 23 }
 24 void dfs(int u,int F,int d)
 25 {
 26     maxD=max(maxD,d);
 27     fa[u][0]=F;
 28     bel[d].push_back(u);
 29     dfn[u]=++ti;
 30     last[u]=back[ti]=u;
 31     dep[u]=d;
 32     for(int i=head[u];i;i=E[i].next)
 33     {
 34         int v=E[i].to;
 35         if(v==F)continue;
 36         dfs(v,u,d+1);
 37         last[u]=last[v];
 38     }
 39 }
 40 void init()
 41 {
 42     for(int i=1;i<=20;++i)
 43         for(int u=1;u<=n;++u)fa[u][i]=fa[fa[u][i-1]][i-1];
 44 }
 45 int lca(int x,int y)
 46 {
 47     if(dep[x]<dep[y])swap(x,y);
 48     int d=dep[x]-dep[y];
 49     for(int i=0;i<=20;++i)
 50         if(d&(1<<i))x=fa[x][i];
 51     if(x==y)return x;
 52     for(int i=20;i>=0;--i)
 53         if(fa[x][i]!=fa[y][i])
 54         {
 55             x=fa[x][i];
 56             y=fa[y][i];
 57         }
 58     return fa[x][0];
 59 }
 60 void insert(int x,int num)
 61 {
 62     T.add(num,1);
 63     if(pre[x].size()==0)
 64     {
 65         pre[x].insert(num);
 66         pre[x].insert(inf);
 67         pre[x].insert(-1);
 68     }
 69     else
 70     {
 71         set<int>::iterator pr=pre[x].lower_bound(num);
 72         set<int>::iterator pl=pr;
 73         --pl;
 74         num=back[num];//特別注意這裡,此時的num已經是節點的編號而不是dfn序了 
 75         if(*pl==-1)T.add(dfn[lca(back[*pr],num)],-1);
 76         else if(*pr==inf)T.add(dfn[lca(back[*pl],num)],-1);
 77         else
 78         {
 79             int u=back[*pl],v=back[*pr];
 80             int l1=lca(u,num),l2=lca(v,num);
 81             if(dep[l1]>dep[l2]) T.add(dfn[l1],-1);
 82             else T.add(dfn[l2],-1);
 83         }
 84         pre[x].insert(dfn[num]);
 85     }
 86 }
 87 int main()
 88 {
 89     ios::sync_with_stdio(false);
 90     cin>>n>>m;
 91     for(int i=1;i<=n;++i)cin>>c[i];
 92     for(int i=1;i<=n-1;++i)
 93     {
 94         cin>>x>>y;
 95         add(x,y);
 96         add(y,x);
 97     }
 98     dfs(1,1,1);
 99     init();
100     for(int i=1;i<=m;++i)
101     {
102         cin>>Q[i].pos>>Q[i].d;
103         Q[i].num=i;
104     }
105     sort(Q+1,Q+m+1,cmp);
106     int pos=1;
107     for(int i=1;i<=n;++i)
108     {
109         for(int j=0;j<bel[i].size();++j)
110         {
111             int u=bel[i][j];
112             insert(c[u],dfn[u]);
113         }
114         while(pos<=m&&min(dep[Q[pos].pos]+Q[pos].d,maxD)==i)
115         {
116             ans[Q[pos].num]=T.ask(dfn[last[Q[pos].pos]])-T.ask(dfn[Q[pos].pos]-1);
117             ++pos;
118         }
119     }
120     for(int i=1;i<=m;++i)cout<<ans[i]<<endl;
121     return 0;
122 }
prob3
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int maxn=1E5+5;
 4 int n,m,head[maxn*2],size,x,y,fa[maxn],c[maxn];
 5 set<int>ans;
 6 struct edge{int to,next;}E[maxn*2];
 7 void add(int u,int v)
 8 {
 9     E[++size].to=v;
10     E[size].next=head[u];
11     head[u]=size;
12 }
13 void init(int u,int F)
14 {
15     fa[u]=F;
16     for(int i=head[u];i;i=E[i].next)
17     {
18         int v=E[i].to;
19         if(v==F)continue;
20         init(v,u);
21     }
22 }
23 void dfs(int u,int F,int d)
24 {
25     ans.insert(c[u]);
26     if(d==0)return;
27     for(int i=head[u];i;i=E[i].next)
28     {
29         int v=E[i].to;
30         if(v==F)continue;
31         dfs(v,u,d-1);
32     }
33 }
34 int main()
35 {
36     ios::sync_with_stdio(false);
37     cin>>n>>m;
38     for(int i=1;i<=n;++i)cin>>c[i];
39     for(int i=1;i<=n-1;++i)
40     {
41         cin>>x>>y;
42         add(x,y);
43         add(y,x);
44     }
45     init(1,1);
46     while(m--)
47     {
48         cin>>x>>y;
49         ans.clear();
50         dfs(x,fa[x],y);
51         cout<<ans.size()<<endl;
52     }
53     return 0;
54 }
prob3-force

 (未填完)


 

Prob 4

樹的問題太難了,我們先想鏈上問題怎麼做。

鏈上問題還是太難了,我們先考慮只有質數怎麼做。

只有質數的話,就轉化為Prob3了。

再考慮鏈上一般的情況。我們只要把每個數質因式分解,對於固定的質數p以及它的次數k,看將它看做1~k種顏色的“和”。如:

其餘的類似。

這樣繼續沿用Prob3,可以推廣到樹上。開個set就行了。

程式碼看博主心情。

相關文章