UOJ268 [清华集训2016] 数据交互 【动态DP】【堆】【树链剖分】【线段树】

题目分析:

不难发现可以用动态DP做。

题目相当于是要我求一条路径,所有与路径有交的链的代价加入进去,要求代价最大。

我们把链的代价分成两个部分:一部分将代价加入$LCA$之中,用$g$数组保存;另一部分将代价加在整条链上,用$d$数组保存。

这时候我们可以发现,一条从$u$到$v$的路径的代价相当于是$d[LCA(u,v)]+\sum_{x \in edge(u,v)}g[x]$。

如果是静态的,可以用树形DP解决。

看过《神奇的子图》的同学都知道,叶子结点是从它的儿子中取两个最大的出来,所以堆维护。

考虑合并。

链从左延申出的最大的$g$的总和记录。链从右延申包括$d$的总和记录,每次向上$update$的时候拼起来与原答案比较即可。

 

代码:

 

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 
  4 typedef long long ll;
  5 
  6 const int maxn = 102000;
  7 
  8 int n,m;
  9 
 10 vector <int> g[maxn];
 11 int sz[maxn],top[maxn],fa[maxn],dep[maxn],son[maxn],ind[maxn],dr[maxn];
 12 int tail[maxn],num;
 13 
 14 struct Query{int from,to,w;}Q[maxn];
 15 struct Priority_Queue{
 16     priority_queue<ll,vector<ll>,less<ll> > pq,del;
 17     void push(ll now){pq.push(now);}
 18     void pop(){
 19     while(!del.empty() && pq.top() == del.top()) pq.pop(),del.pop();
 20     pq.pop();
 21     }
 22     ll top(){
 23     while(!del.empty() && pq.top() == del.top()) pq.pop(),del.pop();
 24     if(pq.empty()) return 0;
 25     else return pq.top();
 26     }
 27     ll sec(){
 28     while(!del.empty() && pq.top() == del.top()) pq.pop(),del.pop();
 29     if(pq.size() ==0) return 0;
 30     ll oop = pq.top(); pq.pop();
 31     while(!del.empty() && pq.top() == del.top()) pq.pop(),del.pop();
 32     if(pq.size() == 0){pq.push(oop);return 0;}
 33     else {ll ret = pq.top();pq.push(oop);return ret;}
 34     }
 35     void Erase(ll now){del.push(now);}
 36 }Son[maxn],Ans;
 37 
 38 struct segmentTree{
 39     ll tg,ff,lazy,REC,L,R;
 40 }T[maxn<<2];
 41 
 42 void push_down(int now){
 43     T[now<<1].ff += T[now].lazy; T[now<<1|1].ff += T[now].lazy;
 44     T[now<<1].REC += T[now].lazy; T[now<<1|1].REC += T[now].lazy;
 45     T[now<<1].lazy += T[now].lazy; T[now<<1|1].lazy += T[now].lazy;
 46     T[now<<1].R += T[now].lazy; T[now<<1|1].R += T[now].lazy;
 47     T[now].lazy = 0;
 48 }
 49 
 50 segmentTree merge(segmentTree alpha,segmentTree beta){
 51     segmentTree RES;RES.lazy = 0;  RES.ff = 0;
 52     RES.tg = alpha.tg + beta.tg;
 53     RES.REC = max(alpha.REC,beta.REC);
 54     RES.REC = max(RES.REC,alpha.R + beta.L);
 55     RES.L = max(alpha.L,alpha.tg + beta.L);
 56     RES.R = max(beta.R,alpha.R + beta.tg);
 57     return RES;
 58 }
 59 
 60 void dfs1(int now,int f,int dp){
 61     dep[now] = dp; fa[now] = f;
 62     int maxx = 0;
 63     for(auto it:g[now]){
 64     if(it == f) continue;
 65     dfs1(it,now,dp+1);
 66     sz[now] += sz[it];
 67     if(maxx == 0 || sz[it] > sz[maxx]) maxx = it;
 68     }
 69     son[now] = maxx; sz[now]++;
 70 }
 71 
 72 void dfs2(int now,int tp){
 73     top[now] = tp; ind[now] = ++num; dr[num] = now;
 74     if(now == tp) Ans.push(0);
 75     if(son[now]) dfs2(son[now],tp);
 76     else tail[tp] = now;
 77     for(auto it : g[now]){
 78     if(it == fa[now] || it == son[now]) continue;
 79     dfs2(it,it);
 80     }
 81 }
 82 
 83 void read(){
 84     scanf("%d%d",&n,&m);
 85     for(int i=1;i<n;i++){
 86     int u,v; scanf("%d%d",&u,&v);
 87     g[u].push_back(v); g[v].push_back(u);
 88     }
 89 }
 90 
 91 int QueryLca(int u,int v){
 92     while(top[u] != top[v]){
 93     if(dep[top[u]] > dep[top[v]]) u = fa[top[u]];
 94     else v = fa[top[v]];
 95     }
 96     if(dep[u] < dep[v]) return u; else return v;
 97 }
 98 
 99 segmentTree Querylen(int now,int tl,int tr,int l,int r){
100     if(tl >= l && tr <= r) return T[now];
101     if(T[now].lazy) push_down(now);
102     int mid = (tl+tr)/2;
103     if(mid < l) return Querylen(now<<1|1,mid+1,tr,l,r);
104     if(mid >= r) return Querylen(now<<1,tl,mid,l,r);
105     segmentTree pp = Querylen(now<<1|1,mid+1,tr,l,r);
106     segmentTree qq = Querylen(now<<1,tl,mid,l,r);
107     return merge(qq,pp);
108 }
109 
110 void AddG(int now,int tl,int tr,int place,int w){
111     if(tl == tr){T[now].tg += w;T[now].L += w;return;}
112     if(T[now].lazy) push_down(now);
113     int mid = (tl+tr)/2;
114     if(mid >= place) AddG(now<<1,tl,mid,place,w);
115     else AddG(now<<1|1,mid+1,tr,place,w);
116     T[now] = merge(T[now<<1],T[now<<1|1]);
117 }
118 
119 void ModifyG(int now,int tl,int tr,int place){
120     if(tl == tr){
121     tl = dr[tl]; T[now].L = Son[tl].top() + T[now].tg;
122     T[now].REC = T[now].ff + Son[tl].top() + Son[tl].sec();
123     T[now].R = T[now].ff + Son[tl].top();
124     return;
125     }
126     if(T[now].lazy) push_down(now);
127     int mid = (tl+tr)/2;
128     if(mid >= place) ModifyG(now<<1,tl,mid,place);
129     else ModifyG(now<<1|1,mid+1,tr,place);
130     T[now] = merge(T[now<<1],T[now<<1|1]);
131 }
132 
133 void ModifyF(int now,int tl,int tr,int l,int r,int w){
134     if(tl >= l && tr <= r){
135     T[now].lazy += w; T[now].ff += w; T[now].R += w; T[now].REC+=w;
136     return;
137     }
138     if(T[now].lazy) push_down(now);
139     int mid = (tl+tr)/2;
140     if(mid >= l) ModifyF(now<<1,tl,mid,l,r,w);
141     if(mid+1 <= r) ModifyF(now<<1|1,mid+1,tr,l,r,w);
142     T[now] = merge(T[now<<1],T[now<<1|1]);
143 }
144 
145 void SingleModify(int now,int w){
146     int hole = tail[top[now]]; 
147     segmentTree fk = Querylen(1,1,n,ind[top[hole]],ind[hole]);
148     AddG(1,1,n,ind[now],w); now=fa[top[now]];
149     while(now){
150     segmentTree rl = Querylen(1,1,n,ind[top[hole]],ind[hole]);
151     Ans.Erase(fk.REC); Ans.push(rl.REC);
152     Son[now].Erase(fk.L); Son[now].push(rl.L);
153     hole = tail[top[now]]; fk = Querylen(1,1,n,ind[top[hole]],ind[hole]);
154     ModifyG(1,1,n,ind[now]); now = fa[top[now]];
155     }
156     Ans.Erase(fk.REC);
157     fk = Querylen(1,1,n,ind[top[hole]],ind[hole]);
158     Ans.push(fk.REC);
159 }
160 
161 void WideModify(int now,int LCA,int w){
162     while(dep[now] >= dep[LCA]){
163     segmentTree fk = Querylen(1,1,n,ind[top[now]],ind[tail[top[now]]]);
164     if(dep[top[now]] < dep[LCA]) ModifyF(1,1,n,ind[LCA],ind[now],w);
165     else ModifyF(1,1,n,ind[top[now]],ind[now],w);
166     Ans.Erase(fk.REC);
167     fk = Querylen(1,1,n,ind[top[now]],ind[tail[top[now]]]);
168     Ans.push(fk.REC);
169     now = fa[top[now]];
170     }
171 }
172 
173 void Modify(int u,int v,int w){
174     int LCA = QueryLca(u,v);
175     SingleModify(LCA,w);
176     WideModify(u,LCA,w); // u
177     WideModify(v,LCA,w); // v
178     WideModify(LCA,LCA,-w); // LCA
179 }
180 
181 void build_tree(int now,int tl,int tr){
182     if(tl == tr){
183     tl = dr[tl];
184     for(auto it : g[tl]){
185         if(it == son[tl] || it == fa[tl]) continue;
186         Son[tl].push(0);
187     }
188     }else{
189     int mid = (tl+tr)/2;
190     build_tree(now<<1,tl,mid); build_tree(now<<1|1,mid+1,tr);
191     }
192 }
193 
194 void work(){
195     dfs1(1,0,1);
196     dfs2(1,1);
197     build_tree(1,1,n);
198     for(int i=1;i<=m;i++){
199     char ch = getchar(); while(ch != '+' && ch != '-') ch = getchar();
200     int fr,t,w;
201     if(ch == '+'){
202         scanf("%d%d%d",&fr,&t,&w);Q[i].from=fr;Q[i].to=t;Q[i].w=w;
203     }else{
204         int x; scanf("%d",&x);fr = Q[x].from,t = Q[x].to,w = -Q[x].w;  
205     }
206     Modify(fr,t,w);
207     printf("%lld\n",Ans.top());
208     }
209 }
210 
211 int main(){
212     read();
213     work();
214     return 0;
215 }

 

转载于:https://www.cnblogs.com/Menhera/p/9347536.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值