hihocoder1954 : 压缩树
首先求出缩一个点 $x$ 的贡献,就是缩 $x$ 的父亲的贡献加上 $x$ 的子树多减少的深度
假设此时缩父亲的贡献已经考虑过了,那么 $x$ 的子树多减少的深度就是子树的节点数
注意此时要满足 $x$ 不是根节点或根节点的儿子,不然缩和没缩是一样的
设这个贡献为 $sum[x]$
然后把所有操作 $l,r,v$ 离线,遇到一个操作左端点就把 $v$ 插入 $set$,遇到右端点再取出,在过程中动态维护总贡献
考虑把每个当前加入的节点搞一个虚树
对于一次缩节点的操作,如果它不在虚树链上,只会影响 $x$ 与虚树的第一个交点的一条链,更上面的已经缩过了
设交点为 $u$,那么贡献就是 $sum[x]-sum[u]$
如果原本已经在虚树链上了,那么 $x$ 不会有贡献
现在问题是如何求与虚树的交点,考虑原本构造虚树的过程,把节点按 $dfn$ 排序,然后根据与前后节点的 $lca$ 确定具体连边
设前后节点为 $u,v$,如果 $LCA(u,x)=u$ 且 $LCA(v,x)=x$ 那么 $x$ 在虚树边 $(u,v)$ 上,不产生贡献
如果 $LCA(u,x)!=u$ 且 $LCA(v,x)=x$ 那么 $x$ 还是在虚树上 $v$ 到根的路径上,不产生贡献
如果 $LCA(u,x)=u$ 且 $LCA(v,x)!=x$ ,或者 $LCA(u,x)!=u$ 且 $LCA(v,x)!=x$ 那么说明 $x$ 有多出来一段不在虚树还没统计的贡献
显然多出来的一段是 $LCA(u,x),LCA(v,x)$ 中深度较大的节点 $p$ 与 $x$ 的一条链的贡献,贡献为 $sum[x]-sum[p]$
然后就可以写成代码实现了,但是发现对于前两种情况 $LCA(u,x),LCA(v,x)$ 中深度较大的节点 $p$ 就是 $x$,贡献其实就是 $sum[x]-sum[p]=0$
所以直接求 $p$ 就行,不用分情况讨论了
因为一个节点可以被多次插入,所以要用 $multiset$,$multiset$ 里节点按 $dfn$ 排序就可以直接找前驱后继了
具体看代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<set>
using namespace std;
typedef long long ll;
inline int read()
{
int x=,f=; char ch=getchar();
while(ch<''||ch>'') { if(ch=='-') f=-; ch=getchar(); }
while(ch>=''&&ch<='') { x=(x<<)+(x<<)+(ch^); ch=getchar(); }
return x*f;
}
const int N=2e5+;
int n,m,Q;
vector <int> V[N],add[N],del[N];
int dfn[N],F[][N],dep[N],sz[N],cnt;
ll sum[N],ans;
void dfs1(int x)
{
sz[x]=; dfn[x]=++cnt; ans+=dep[x]-;
for(int i=;i<=;i++) F[i][x]=F[i-][F[i-][x]];
for(int v : V[x])
{
if(v==F[][x]) continue;
F[][v]=x; dep[v]=dep[x]+;
dfs1(v); sz[x]+=sz[v];
}
}
void dfs2(int x)
{
sum[x]=sum[F[][x]]+(dep[x]>)*sz[x];
for(int v : V[x]) if(v!=F[][x]) dfs2(v);
}
inline int LCA(int x,int y)
{
if(dep[x]<dep[y]) swap(x,y);
for(int i=;i>=;i--)
if(dep[F[i][x]]>=dep[y]) x=F[i][x];
if(x==y) return x;
for(int i=;i>=;i--)
if(F[i][x]!=F[i][y]) x=F[i][x],y=F[i][y];
return F[][x];
}
//以上预处理一堆东西
struct dat{
int val,x;
dat (int v=,int xx=) { val=v,x=xx; }
inline bool operator < (const dat &tmp) const {
return val<tmp.val;
}
};
multiset <dat> S;
multiset <dat>::iterator it;
inline int Find(int x)//求x与当前虚树的交点p
{
dat res; it=S.upper_bound(dat(dfn[x],x)); int lca;
//注意上面的res和set里面的节点没有关系,只是为了方便更新res
if(it!=S.end())//注意判越界
lca=LCA(x,(*it).x),res=max(res,dat(dep[lca],lca));
if(it!=S.begin())
lca=LCA(x, (*prev(it)).x ),res=max(res,dat(dep[lca],lca));
//上面的 prev(it) 是找到与it-1指针不同的最后一个位置
return res.x ? res.x : x;
}
int main()
{
n=read(),m=read(),Q=read(); int a,b,c;
for(int i=;i<n;i++)
{
a=read(),b=read();
V[a].push_back(b); V[b].push_back(a);
}
while(Q--)
{
a=read(),b=read(),c=read();
add[a].push_back(c); del[b+].push_back(c);
}
dep[]=; dfs1(); dfs2(); S.insert(dat(dfn[],));//初始有根节点
for(int i=;i<=m;i++)
{
for(int x : add[i])
{
if(S.find(dat(dfn[x],x))==S.end()) ans-=(sum[x]-sum[Find(x)]);//第一次插入
S.insert(dat(dfn[x],x));
}
for(int x : del[i])
{
S.erase(S.find( dat(dfn[x],x) ));
if(S.find(dat(dfn[x],x))==S.end()) ans+=(sum[x]-sum[Find(x)]);//同理
}
printf("%lld ",ans);
}
printf("\n");
return ;
}
最新文章
- 阿里云 SDK python3支持
- 定位 position: absolute &; relative
- 如何在Ubuntu中让mongo远程可连接
- C# 反射/映射学习
- Using dblink in Postgres
- js 用window.open(参数) 打开新窗口,在新窗口怎么获取传过来的参数
- Android进阶笔记05:View、SurfaceView 和GLSurfaceView 的关系和区别
- PHP的语言规范
- linux内核中分配4M以上大内存的方法
- Teacher YYF - POJ 3746(打表........)
- 对于没有Command属性时,怎么来达到相同的效果
- css透明度(兼容所有浏览器)
- (中等) CF 585B Phillip and Trains,BFS。
- 通知栏Notification的整理
- selenium python自动化测试 ddt数据驱动
- 一次saltstack环境变量的坑
- shell常用脚本
- jdk各个版本的新特性(jdk1.7,1.8,1.9)
- ABP实践学习
- js扩展运算符(spread)三个点(...)
热门文章
- JavaSE---显式锁
- 【Luogu5293】[HNOI2019] 白兔之舞
- Python---进阶---捕获异常
- os模块、sys模块、json模块、pickle模块、logging模块
- MySQL 数据库慢查询日志分析脚本
- 接口返回[object,Object]解决方法
- php 处理错误和异常技巧
- php 判断访问是否是手机或者pc
- Parse error: syntax error, unexpected &#39;class&#39; (T_CLASS)
- Gradle 编译加速