P4338-[ZJOI2018]历史【LCT】
正题
题目链接:https://www.luogu.com.cn/problem/P4338
题目大意
给出\(n\)个点的一棵树,和每个点进行\(access\)的次数\(a_i\),要求安排一个顺序使得虚实边转换最多。
\(m\)次修改一个点让\(a_i\)加上\(w\)后求答案
\(n,m\in [1,4*10^5],a_i,w\in[1,10^7]\)
解题思路
好像本来就很麻烦还带修改,那先不考虑修改
考虑统计每个节点下的边的虚实切换最大化,可以发现一个节点的子树中无论怎样安排\(access\)顺序也不会影响外面的答案,因为对于外面的来说都相当于\(access\)了这个节点。
所以这个满足子最优?(好像是这么叫的),那每一个节点的分块考虑就好了。现在对于这个节点下的边,如果两次\(access\)的是在不同的儿子的子树中就会产生一点贡献。
转换一下现在的问题就是有若干种个数不同的颜色排成一排,要求相邻的异色最多。这个可以贪心解决,正常来说只要每次拿与上个不同的最多的来排就能到达\(sum-1\)的答案上线,但是需要特判一下如果最多的颜色个数\(mx\)有\(2\times mx>sum\)那么此时这样排到最后还有一种颜色剩下,答案就是\(2\times (sum-mx)\)。
所以一个节点的答案就是\(min\{sum-1,2\times (sum-mx)\}\)
但是带修改怎么搞,考虑到每次修改一定是加一个正权。
我们显然有一个式子
\]
这个式子表明如果一个节点选择了\(2\times (sum-mx)\)作为权值,那么它以后也都是这个权值。
\(s_x\)表示\(x\)子树中的权值和,对一个节点\(x\)定义\(r=max\{s_y(fa_y=x)\}\)和\(sum=\sum_{fa_y=x}s_y\)
如果\(2\times mx>sum\)那么向\((x,y)\)连一条实边,其他儿子连虚边。否则全连虚边。
那么每次修改的过程中我们只需要遍历到根节点的虚边看是否需要切换即可,这个可以用\(LCT\)来维护。
而且因为每条虚边代表着\(2\times s_y\leq s_x\),所以路径上虚边的个数不会超过\(\log \sum a_i\)级别。
时间复杂度\(O(n\log \sum a_i)\)(\(Splay\)那个\(\log n\)因为小于\(\log \sum a_i\)就舍去)
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=4e5+10;
struct node{
ll to,next;
}a[N<<1];
ll n,m,s[N],ls[N],ans,tot;
struct LCT{
ll fa[N],s[N],w[N],v[N],t[N][2];
bool Nroot(ll x)
{return fa[x]&&(t[fa[x]][0]==x||t[fa[x]][1]==x);}
bool Direct(ll x){return t[fa[x]][1]==x;}
void PushUp(ll x)
{s[x]=s[t[x][0]]+s[t[x][1]]+w[x]+v[x];return;}
void Rotate(ll x){
ll y=fa[x],z=fa[y];
ll xs=Direct(x),ys=Direct(y);
ll w=t[x][xs^1];
t[x][xs^1]=y;t[y][xs]=w;
if(Nroot(y))t[z][ys]=x;
fa[x]=z;fa[y]=x;if(w)fa[w]=y;
PushUp(y);PushUp(x);
return;
}
void Splay(ll x){
while(Nroot(x)){
ll y=fa[x];
if(!Nroot(y))Rotate(x);
else if(Direct(x)==Direct(y))
Rotate(y),Rotate(x);
else Rotate(x),Rotate(x);
}
return;
}
ll ct(ll x,ll r,ll h){
if(t[x][1])return (r-h)*2;
return min(r-1,(r-v[x])*2);
}
void Access(ll x,ll c){
Splay(x);
ll r=s[x]-s[t[x][0]],h=s[t[x][1]];
ans-=ct(x,r,h);v[x]+=c;r+=c;PushUp(x);
if(h*2<r+1)w[x]+=h,t[x][1]=0;
ans+=ct(x,r,h);PushUp(x);ll y;
for(y=x,x=fa[x];x;y=x,x=fa[x]){
Splay(x);
ll r=s[x]-s[t[x][0]],h=s[t[x][1]];
ans-=ct(x,r,h);w[x]+=c;r+=c;
if(h*2<r+1)w[x]+=h,t[x][1]=h=0;
if(s[y]*2>r)w[x]-=s[y],t[x][1]=y,h=s[y];
ans+=ct(x,r,h);PushUp(x);
}
return;
}
}T;
void addl(ll x,ll y){
a[++tot].to=y;
a[tot].next=ls[x];
ls[x]=tot;return;
}
void dp(ll x,ll fa){
ll son=0,mx=T.v[x]=s[x];T.fa[x]=fa;
for(ll i=ls[x];i;i=a[i].next){
ll y=a[i].to;
if(y==fa)continue;
dp(y,x);s[x]+=s[y];
if(s[y]>mx)son=y,mx=s[y];
}
ans+=min(s[x]-1,(s[x]-mx)*2);
if(mx*2>s[x])T.t[x][1]=son;
T.w[x]=s[x]-T.v[x]-s[T.t[x][1]];
T.s[x]=s[x];return;
}
signed main()
{
scanf("%lld%lld",&n,&m);
for(ll i=1;i<=n;i++)
scanf("%lld",&s[i]);
for(ll i=1;i<n;i++){
ll x,y;
scanf("%lld%lld",&x,&y);
addl(x,y);addl(y,x);
}
dp(1,0);printf("%lld\n",ans);
for(ll i=1;i<=m;i++){
ll x,w;
scanf("%lld%lld",&x,&w);
T.Access(x,w);
printf("%lld\n",ans);
}
return 0;
}
P4338-[ZJOI2018]历史【LCT】的更多相关文章
- P4338 [ZJOI2018]历史 LCT+树形DP
\(\color{#0066ff}{ 题目描述 }\) 这个世界有 n 个城市,这 n 个城市被恰好 \(n-1\) 条双向道路联通,即任意两个城市都可以 互相到达.同时城市 1 坐落在世界的中心,占 ...
- 洛谷P4338 [ZJOI2018]历史(LCT,树形DP,树链剖分)
洛谷题目传送门 ZJOI的考场上最弱外省选手T2 10分成功滚粗...... 首先要想到30分的结论 说实话Day1前几天刚刚刚掉了SDOI2017的树点涂色,考场上也想到了这一点 想到了又有什么用? ...
- Luogu4338 ZJOI2018 历史 LCT、贪心
传送门 题意:在$N$个点的$LCT$中,最开始每条边的虚实不定,给出每一个点的$access$次数,求一种$access$方案使得每条边的虚实变换次数之和最大,需要支持动态增加某个点的$access ...
- 【BZOJ5212】[ZJOI2018]历史(Link-Cut Tree)
[BZOJ5212][ZJOI2018]历史(Link-Cut Tree) 题面 洛谷 BZOJ 题解 显然实际上就是给定了一棵树和每个点被\(access\)的次数,求解轻重链切换的最大次数. 先考 ...
- [ZJOI2018]历史
[ZJOI2018]历史 最大化access轻重链的切换次数 考虑一个点的贡献,即它交换重儿子的次数 发现这个次数只和它自己ai以及每个儿子的子树次数和有关. 一个关键的事实是: 我们可以自上而下进行 ...
- BZOJ5212 ZJOI2018历史(LCT)
首先相当于最大化access的轻重边交换次数. 考虑每个点作为战场(而不是每个点所代表的国家与其他国家交战)对答案的贡献,显然每次产生贡献都是该点的子树内(包括自身)此次access的点与上次acce ...
- [ZJOI2018]历史(LCT)
这篇还发了洛谷题解 [Luogu4338] [BZOJ5212] 题解 题意 给出一棵树,给定每一个点的 \(access\) 次数,计算轻重链切换次数的最大值,带修改. 先考虑不带修改怎么做 假设 ...
- 【BZOJ5212】[ZJOI2018] 历史(LCT大黑题)
点此看题面 大致题意: 给定一棵树每个节点\(Access\)的次数,求最大虚实链切换次数,带修改. 什么是\(Access\)? 推荐你先去学一学\(LCT\)吧. 初始化(不带修改的做法) 首先考 ...
- LOJ2434. 「ZJOI2018」历史 [LCT]
LOJ 思路 第一眼看似乎没有什么思路,试着套个DP上去:设\(dp_x\)表示只考虑\(x\)子树,能得到的最大答案. 合并的时候发现只有\(x\)这个点有可能做出新的贡献,而做出新贡献的时候必然是 ...
- bzoj 5212: [Zjoi2018]历史
Description 九条可怜是一个热爱阅读的女孩子. 这段时间,她看了一本非常有趣的小说,这本小说的架空世界引起了她的兴趣. 这个世界有n个城市,这n个城市被恰好n?1条双向道路联通,即任意两个城 ...
随机推荐
- 基于taro封装底下浮动弹窗组件
先看效果图: jsx: import Taro, { Component } from '@tarojs/taro' import { View, Image } from '@tarojs/comp ...
- JDBC高级篇(MYSQL)—— JDBC中初涉数据库事务
注意:其中的JdbcUtil是我自定义的连接工具类:代码例子链接: package d_transaction; import java.sql.Connection; import java.sql ...
- 【MATLAB】常用命令快速入门,国赛加油
矩阵运算 矩阵的基本生成 m1 = 1:5 % 生成行矩阵[1,2,3,4,5] m2 = 1:2:10 % 起点:步长:终点 [1,3,5,7,9] linspace(x1,x2,n) % 生成 n ...
- netty系列之:自建客户端和HTTP服务器交互
目录 简介 使用客户端构建请求 accept-encoding server解析HTTP请求 总结 简介 上一篇文章,我们搭建了一个支持中文的HTTP服务器,并且能够从浏览器访问,并获取到相应的结果. ...
- 新东方集团K12公益免费课战役记
作者:张建鑫, 曾任IBM高级软件架构师, 滴滴高级技术专家, 现任新东方集团高级技术总监 1月31日,集团领导决定由产品技术中心的新东方APP团队牵头做周一到周五的集团公益课, 提供给全国中小学生使 ...
- Djangoform组件——ModelForm的基本使用
from django.contrib import admin from django.urls import path from app01 import views urlpatterns = ...
- 「山东省队集训2021 Round 1」 半夜
考虑将 \(X\) 复制一次放到后面再对其长度为 \(n\) 的连续子串和 \(Y\) 求一波 \(\rm{Longest\ Common\ Subsequence}\) 就能得到 \(\Theta( ...
- 【Python机器学习实战】决策树与集成学习(六)——集成学习(4)XGBoost原理篇
XGBoost是陈天奇等人开发的一个开源项目,前文提到XGBoost是GBDT的一种提升和变异形式,其本质上还是一个GBDT,但力争将GBDT的性能发挥到极致,因此这里的X指代的"Extre ...
- Git 系列教程(2)- Git 安装
前言 直接复制官网的教程了,不对自己百度吧,不然就参考下我的几篇文章 Linux安装Git(源码安装) https://www.cnblogs.com/poloyy/p/12186802.html 在 ...
- .Net性能调优-ArrayPool
定义 高性能托管数组缓冲池,可重复使用,用租用空间的方式代替重新分配数组空间的行为 好处 可以在频繁创建和销毁数组的情况下提高性能,减少垃圾回收器的压力 使用 获取缓冲池实例:Create/Share ...