洛谷題目

看到題面,很容易就想到,這是要你找樹上的重心,只不過這個重心是在帶邊權的樹上

所以對於這個我們在樹上找這個重心

一開始我想的是,我要更新權值,然後把每個點的答案更新一下

就取最大值,這好像是O(....),我也不會算這個複雜度,好像太大了

後來去學習了一下,點分樹;;

知道了有點分樹這個東西,立刻就想到

如果我們在點分樹上尋找這個帶權重心,是不是就可以保證複雜度了???

我先去打了板子,就是上一篇部落格,一開始我就想用樹狀陣列

然後我發現自己狹隘了,用樹狀陣列根本維護不了每個點的軍隊數量

然後我就想別的辦法,想不出來,頹一發題解,可是剛看到題解,他竟然沒用樹狀陣列,而是維護了兩個陣列,一個存答案,一個存數量

和樹狀陣列的思想類似,還是要維護兩個值,維護自己的,維護爹的,不過這裡還有另外一個套路

  ans加上爹的,減去自己的,還要加上x到爹的價值

然後還有一道題也是這樣維護,沒啥意思,不過那個題調了一晚上加上一早起(MLE+TLE),為啥呢? 手殘打錯一個變數;

所以這個題就這樣做,加入軍隊就是在點分樹上更新,然後記錄一下第一次樹的重心,然後由這個樹根沿著點分樹的父子關係向下搜尋

所以我們還要開另外一個臨界表來儲存他們的兒子;

記得我們判斷答案是否在他這個兒子裡,要在原樹上判斷,一開始就在點分樹上判斷,WA了好幾遍

然後搜尋的時候,要回到點分樹上搜索。。。。

  1 #include<bits/stdc++.h>
2 using namespace std;
3 #define int long long
4 #define re register int
5 const int N=100005;
6 int n,m;
7 int to[N<<1],nxt[N<<1],val[N<<1],head[N],rp;
8 void add_edg(int x,int y,int z){
9 to[++rp]=y;val[rp]=z;nxt[rp]=head[x];head[x]=rp;
10 }
11 int siz[N],son[N],dep[N],len[N],fa[N],top[N];
12 void dfs1(int x,int f){
13 siz[x]=1;
14 dep[x]=dep[f]+1;fa[x]=f;
15 for(re i=head[x];i;i=nxt[i]){
16 int y=to[i];
17 if(y==f)continue;
18 len[y]=len[x]+val[i];
19 dfs1(y,x);
20 siz[x]+=siz[y];
21 if(!son[x]||siz[y]>siz[son[x]])son[x]=y;
22 }
23 }
24 void dfs2(int x,int f){
25 top[x]=f;
26 if(son[x])dfs2(son[x],f);
27 for(re i=head[x];i;i=nxt[i]){
28 int y=to[i];
29 if(y==fa[x]||y==son[x])continue;
30 dfs2(y,y);
31 }
32 }
33 int get_lca(int x,int y){
34 while(top[x]!=top[y]){
35 if(dep[top[x]]<dep[top[y]])swap(x,y);
36 x=fa[top[x]];
37 }
38 return dep[x]<dep[y]?x:y;
39 }
40 int get_dis(int x,int y){
41 return len[x]+len[y]-2*len[get_lca(x,y)];
42 }
43 int rt,alsiz,mx,ms[N];
44 bool vis[N];
45 void get_rt(int x,int f){
46 siz[x]=1;ms[x]=0;
47 for(re i=head[x];i;i=nxt[i]){
48 int y=to[i];
49 if(y==f||vis[y])continue;
50 get_rt(y,x);
51 siz[x]+=siz[y];
52 ms[x]=max(ms[x],siz[y]);
53 }
54 ms[x]=max(ms[x],alsiz-siz[x]);
55 if(ms[x]<mx)mx=ms[x],rt=x;
56 }
57 void get_siz(int x,int f){
58 siz[x]=1;
59 for(re i=head[x];i;i=nxt[i]){
60 int y=to[i];
61 if(y==f||vis[y])continue;
62 get_siz(y,x);
63 siz[x]+=siz[y];
64 }
65 }
66 int newfa[N];
67 int t[N<<1],nx[N<<1],he[N<<1],va[N<<1],r;
68 void add_ed(int x,int y,int z){
69 t[++r]=y;
70 va[r]=z;
71 nx[r]=he[x];
72 he[x]=r;
73 }
74 void pre_dfs(int x){
75 vis[x]=1;get_siz(x,0);
76 for(re i=head[x];i;i=nxt[i]){
77 //cout<<"sb"<<rt<<endl;
78 int y=to[i];
79 if(vis[y])continue;
80 alsiz=mx=siz[y];get_rt(y,0);
81 add_ed(x,y,rt);
82 newfa[rt]=x;pre_dfs(rt);
83 }
84 }
85 int sum1[N],sum2[N];
86 int siz1[N],siz2[N];
87 void get_up(int x,int v){
88 for(re i=x;i;i=newfa[i])sum1[i]+=get_dis(i,x)*v,siz1[i]+=v;
89 for(re i=x;newfa[i];i=newfa[i])sum2[i]+=get_dis(newfa[i],x)*v,siz2[i]+=v;
90 }
91 int get_sum(int x){
92 int ret=sum1[x];
93 for(re i=x;newfa[i];i=newfa[i]){
94 ret+=sum1[newfa[i]];
95 ret-=sum2[i];
96 ret+=get_dis(newfa[i],x)*(siz1[newfa[i]]-siz2[i]);
97 }
98 return ret;
99 }
100 int get_ans(int x){
101 int ret=get_sum(x);
102 for(re i=he[x];i;i=nx[i]){
103 if(get_sum(t[i])<ret)return get_ans(va[i]);
104 }
105 return ret;
106 }
107 signed main(){
108 scanf("%lld%lld",&n,&m);
109 for(re i=1;i<n;i++){
110 int x,y,z;
111 scanf("%lld%lld%lld",&x,&y,&z);
112 add_edg(x,y,z);add_edg(y,x,z);
113 }
114 dfs1(1,0);
115 dfs2(1,1);
116 alsiz=mx=n;
117 get_rt(1,0);
118 //cout<<rt<<endl;
119 pre_dfs(rt);
120 memset(vis,0,sizeof(vis));
121 alsiz=mx=n;
122 get_rt(1,0);
123 //cout<<rt<<endl;
124 for(re i=1;i<=m;i++){
125 int x,y;
126 scanf("%lld%lld",&x,&y);
127 get_up(x,y);
128 //cout<<rt<<" ";
129 printf("%lld\n",get_ans(rt));
130 }
131 }

所以對於點分樹的題,第一找好這個題尋找的是啥

第二搞對容斥,就上面那個公式

第三打程式碼得細心,細節太多了

這類題多會求lca,我習慣用樹鏈剖分,好大也好看

注意距離和深度不是一回事,找lca用深度,找dis用距離(權值);