P5327 [ZJOI2019]语言

解题思路

暴力

首先讲一下我垃圾的 40pts 的暴力(其他 dalao 都是 60pts 起步):

当然评测机快的话(比如 LOJ 的),可以卡过 3,4 个点(逃。

对于 1,2 测试点的话,我们直接记录两个节点之间路径上的所有点,然后用一个二维数组存一下两个点是否能互相贸易。

最后暴力求 ans 就好了。。

然后我们看到了链的部分分,然后就是在序列上的处理了:

对于每一个操作,我们记录下左右端点,然后按照左端点为第一关键字,右端点为第二关键字进行排序。

把各个操作分成若干组,保证每一组的最左端的点比前一组的所有的右端点都要大。

然后对于不同组的第一个直接给答案加上\(C_{r-l+1}^2\),也就是\(\dfrac{(r-l)\times(r-l+1)}{2}\)。

对于同一组的,如果该区间在本组此前的区间内,那么它就没有贡献。

否则把它在组内的长度乘上在组外的长度还有组外边长度的自由组合。

设此时这一组的右端点是 maxr,贡献就是\((r-maxr)\times(maxr-l)+\dfrac{(r-maxr)\times(r-maxr+1)}{2}\)。

\(code\)

正解

正解的做法就比较神仙了,算法方面就是线段树合并+动态开点+树上差分,在加上一点虚树的思想。

对于每个点建一棵线段树,然后再树上差分线段树合并就是各个节点对于答案的贡献了,因此,现在的问题在于对每个节点的处理。

不难发现有以下性质:

对于所有可以与 x 贸易的点实际上就构成了一个生成树,也可以叫做联通块。

如果点 s 和 t 的路径会经过点 x ,那么我们称 s 和 t 为 x 的两个极远点,那么就有了以下结论:

x 的生成树大小其实就是能把 x 的所有极远点的最小生成树。

为了方便,我们硬点存在极远点 1 ,最后再除去 1 的贡献。

然后,如果我们现在需要把 y 点加入到 x 的生成树里,其实就是需要把该生成树里距离 y 最近的点与 y 连起来。

假设那个点是 z ,那么 y 的贡献就是\(dep_y-dep_{\operatorname{lca}(y,z)}\)。

对于 1 点的贡献其实就是所有点的 lca 的 dep 之和。

在线段树上进行操作时,存储四个值:操作数,两个极远点,贡献

接下来就是转移了,因为线段树是建立在时间戳上的,所以对于一个区间来说,两个极远点一定分别来自左右两个子区间。

对于两个子区间在向上更新时不能单纯的只记为两个区间的加和,贡献还应该算上两个子区间之间最近的节点连接起来的贡献,也就是这两个节点的 lca 的 dep 值。

最后就是实现细节了:注意下时间戳和标号之间的转化就好了。

对于求 lca 的时候可以用 \(\mathcal{O}(1)\)查询的优秀 RMQ-ST 算法 但是貌似,树链剖分的 \(\mathcal{O}(n)\)预处理更快一些,其实都大同小异了。。

code

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+10,M=N<<1;
int n,m,tot,ans,root[N];
int tim,dfn[N],id[N],siz[N],son[N],dep[N],topp[N],fa[N];
vector<int> del[N];
struct Edge
{
int tot,head[N],nxt[M],ver[M];
void add(int x,int y)
{
ver[++tot]=y;
nxt[tot]=head[x];
head[x]=tot;
}
}e;
struct Vector_Tree
{
int l,r,s,t,dat,f;
}tre[N*80];
void pre_dfs(int x)
{
siz[x]=1;
for(int i=e.head[x];i;i=e.nxt[i])
{
int to=e.ver[i];
if(to==fa[x])
continue;
fa[to]=x;
dep[to]=dep[x]+1;
pre_dfs(to);
siz[x]+=siz[to];
if(siz[to]>siz[son[x]])
son[x]=to;
}
}
void pre_dfs(int x,int tp)
{
topp[x]=tp;
dfn[x]=++tim;
id[tim]=x;
if(son[x])
pre_dfs(son[x],tp);
for(int i=e.head[x];i;i=e.nxt[i])
if(!dfn[e.ver[i]])
pre_dfs(e.ver[i],e.ver[i]);
}
int LCA(int x,int y)
{
if(!x||!y) return 0;
while(topp[x]^topp[y])
{
if(dep[topp[x]]<dep[topp[y]])
swap(x,y);
x=fa[topp[x]];
}
if(dep[x]>dep[y])
swap(x,y);
return x;
}
void push_up(int x)
{
tre[x].s=(tre[tre[x].l].s?tre[tre[x].l].s:tre[tre[x].r].s);
tre[x].t=(tre[tre[x].r].t?tre[tre[x].r].t:tre[tre[x].l].t);
if(!tre[tre[x].l].t||!tre[tre[x].r].s) tre[x].f=tre[tre[x].l].f+tre[tre[x].r].f;
else tre[x].f=tre[tre[x].l].f+tre[tre[x].r].f-dep[LCA(tre[tre[x].l].t,tre[tre[x].r].s)];
}
void insert(int &x,int l,int r,int pos,int num)
{
if(!x) x=++tot;
if(l==r)
{
tre[x].dat+=num;
tre[x].s=tre[x].t=(tre[x].dat?id[pos]:0);
tre[x].f=(tre[x].dat?dep[id[pos]]:0);
return ;
}
int mid=(l+r)>>1;
if(pos<=mid) insert(tre[x].l,l,mid,pos,num);
else insert(tre[x].r,mid+1,r,pos,num);
push_up(x);
}
void merge(int &x,int y,int l,int r)
{
if(!x||!y)
{
x|=y;
return ;
}
if(l==r)
{
tre[x].dat+=tre[y].dat;
tre[x].f=(tre[x].f?tre[x].f:tre[y].f);
tre[x].s=(tre[x].s?tre[x].s:tre[y].s);
tre[x].t=(tre[x].t?tre[x].t:tre[y].t);
return ;
}
int mid=(l+r)>>1;
merge(tre[x].l,tre[y].l,l,mid);
merge(tre[x].r,tre[y].r,mid+1,r);
push_up(x);
}
int query(int x)
{
return tre[x].f-dep[LCA(tre[x].s,tre[x].t)];
}
void redfs(int x)
{
for(int i=e.head[x];i;i=e.nxt[i])
if(e.ver[i]!=fa[x])
redfs(e.ver[i]);
for(int i=0;i<del[x].size();i++)
insert(root[x],1,tim,dfn[del[x][i]],-1);
ans+=query(root[x]);
merge(root[fa[x]],root[x],1,tim);
}
#undef int
int main()
{
#define int register long long
#define ll long long
scanf("%lld%lld",&n,&m);
for(int i=1,x,y;i<n;i++)
{
scanf("%lld%lld",&x,&y);
e.add(x,y);
e.add(y,x);
}
pre_dfs(1);
pre_dfs(1,1);
for(int i=1,x,y;i<=m;i++)
{
scanf("%lld%lld",&x,&y);
insert(root[x],1,tim,dfn[x],1);
insert(root[x],1,tim,dfn[y],1);
insert(root[y],1,tim,dfn[x],1);
insert(root[y],1,tim,dfn[y],1);
int lca=LCA(x,y);
del[lca].push_back(x);
del[lca].push_back(y);
del[fa[lca]].push_back(x);
del[fa[lca]].push_back(y);
}
redfs(1);
printf("%lld\n",ans/2ll);
return 0;
}

最新文章

  1. 解决VS调试时断点不会命中
  2. Linux命令dos2unix 从windows转换到linux --- nuix2dos从linux转换到windows
  3. codevs3145 汉诺塔问题
  4. Javascript的逻辑判断和循环的知识点
  5. 用Handler图片轮播练习
  6. axure复用-自定义组件,母版(模板)
  7. Protobuf从安装到配置整理帖
  8. HTTP编程(六)
  9. StringBuffer与StringBuilder原理与区别
  10. tmux简要介绍
  11. vue2.0 实现全选和全不选
  12. [置顶]生鲜配送管理系统_升鲜宝V2.0 销售订单汇总_采购任务分配功能_操作说明
  13. VMware与Centos系统安装、重置root密码
  14. Noxim配置运行
  15. 利用PCA进行故障监测
  16. (原)使用1080Ti显卡时安装ubuntu16.04.1及驱动的步骤
  17. Android访问网络,使用HttpURLConnection还是HttpClient?
  18. C语言指针数组(每个元素都是指针)
  19. jQuery基础入门
  20. Hbase 参数配置及优化

热门文章

  1. zip密码破解小脚本
  2. [刷题] 3 Longest Substring Without Repeating Character
  3. Linux 操作系统(二)搜索文件命令find、locate、which、whereis、grep、wc
  4. python-cmdb资产管理项目4-资产入库处理以及资产变更记录处理
  5. (转)Linux下用户组、文件权限详解
  6. gin中间件推荐
  7. 记录: 解决 pycurl: libcurl link-time ssl backend (openssl) is different from compile-time ssl backend (none/other)
  8. Python+Selenium+Appium+API学习使用过的命令
  9. CVPR2020论文解读:三维语义分割3D Semantic Segmentation
  10. 蓝牙mesh网络技术的亮点