ZJOI2019Day2的温暖题,然后考场上只会大常数的\(O(n\log^3 n)\),就懒得写拿了60pts走人

首先我们简化题意,容易发现每个点能到达的点形成了一个联通块,我们只需要统计出这个联通块的大小即可

再进一步,我们发现如果把每条经过\(x\)的路径\((u,v)\)上的两个端点取出,并且维护它们之间的最小生成树,这棵生成树的大小就是最后的答案(可以画图或是感性理解)

接下来就考虑怎么维护每个点出去的生成树大小,首先我们强制选择\(1\)号点,然后用类似于建虚树的方法,每次加入一个新的点就通过LCA来计算距离,从而推出生成树的大小

所以大致思路也有了,我们需要一个能维护区间的支持插入删除的数据结构,那么很容易想到用线段树

那么怎么维护经过一个点的所有路径呢,其实很套路,因为这里线段树上的基本信息就是一个点的存在与否,因此可以树上差分

具体的,对于一条路径\((u,v)\),我们在以\(1\)为根的树上将\(u,v\)两点打上标记,然后在\(\operatorname{LCA}(u,v),father_{\operatorname{LCA}(u,v}\)上删除即可

离线之后就是套路的线段树合并了,然后中间转移有一个求\(\operatorname{LCA}\)的过程,用欧拉序+RMQ即可做到\(O(n\log n)\)的复杂度

CODE

#include<cstdio>
#include<cctype>
#include<vector>
#define RI register int
#define CI const int
#define Tp template <typename T>
#define pb push_back
using namespace std;
const int N=200005;
struct edge
{
int to,nxt;
}e[N<<1]; vector <int> tag[N]; long long ans;
int head[N],n,m,cnt,x,y,rt[N],dfn[N],anc[N],dep[N],idx;
inline void addedge(CI x,CI y)
{
e[++cnt]=(edge){y,head[x]}; head[x]=cnt;
e[++cnt]=(edge){x,head[y]}; head[y]=cnt;
}
class FileInputOutput
{
private:
static const int S=1<<21;
#define tc() (A==B&&(B=(A=Fin)+fread(Fin,1,S,stdin),A==B)?EOF:*A++)
char Fin[S],*A,*B;
public:
Tp inline void read(T& x)
{
x=0; char ch; while (!isdigit(ch=tc()));
while (x=(x<<3)+(x<<1)+(ch&15),isdigit(ch=tc()));
}
#undef tc
}F;
#define to e[i].to
class Euler_Order_On_Tree
{
private:
static const int P=18;
int f[N<<1][P],log[N];
inline int mindep(CI x,CI y)
{
return dep[x]<dep[y]?x:y;
}
inline void swap(int& x,int& y)
{
int t=x; x=y; y=t;
}
public:
inline void DFS(CI now,CI fa=0)
{
anc[now]=fa; dep[now]=dep[fa]+1; f[++idx][0]=now; dfn[now]=idx;
for (RI i=head[now];i;i=e[i].nxt) if (to!=fa) DFS(to,now),f[++idx][0]=now;
}
inline void init(void)
{
RI i,j; for (log[0]=-1,i=1;i<=idx;++i) log[i]=log[i>>1]+1;
for (j=1;j<P;++j) for (i=1;i+(1<<j)-1<=idx;++i)
f[i][j]=mindep(f[i][j-1],f[i+(1<<j-1)][j-1]);
}
inline int getLCA(int x,int y)
{
if (!x||!y) return 0; x=dfn[x]; y=dfn[y]; if (x>y) swap(x,y);
int k=log[y-x+1]; return mindep(f[x][k],f[y-(1<<k)+1][k]);
}
}T;
class Segment_Tree
{
private:
static const int P=25;
struct segment
{
int ch[2],mi,mx,size;
}node[N*P]; int c[N*P],tot;
#define lc(x) node[x].ch[0]
#define rc(x) node[x].ch[1]
#define L(x) node[x].mi
#define R(x) node[x].mx
#define S(x) node[x].size
inline void pushup(CI now)
{
S(now)=S(lc(now))+S(rc(now))-dep[T.getLCA(R(lc(now)),L(rc(now)))];
L(now)=L(lc(now))?L(lc(now)):L(rc(now)); R(now)=R(rc(now))?R(rc(now)):R(lc(now));
}
public:
inline void modify(int& now,CI p,CI mv,CI l=1,CI r=idx)
{
if (!now) now=++tot; if (l==r)
return (void)(c[now]+=mv,S(now)=c[now]?dep[p]:0,L(now)=R(now)=c[now]?p:0);
int mid=l+r>>1; if (dfn[p]<=mid) modify(lc(now),p,mv,l,mid);
else modify(rc(now),p,mv,mid+1,r); pushup(now);
}
inline void merge(int& x,CI y,CI l=1,CI r=idx)
{
if (!x||!y) return (void)(x|=y); if (l==r)
return (void)(c[x]+=c[y],S(x)|=S(y),L(x)|=L(y),R(x)|=R(y)); int mid=l+r>>1;
merge(lc(x),lc(y),l,mid); merge(rc(x),rc(y),mid+1,r); pushup(x);
}
inline int query(CI now)
{
return S(now)-dep[T.getLCA(L(now),R(now))];
}
#undef lc
#undef rc
#undef L
#undef R
#undef S
}SEG;
inline void DFS(CI now,CI fa=0)
{
for (RI i=head[now];i;i=e[i].nxt) if (to!=fa) DFS(to,now);
for (int it:tag[now]) SEG.modify(rt[now],it,-1);
ans+=SEG.query(rt[now]); SEG.merge(rt[anc[now]],rt[now]);
}
#undef to
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
RI i; for (F.read(n),F.read(m),i=1;i<n;++i) F.read(x),F.read(y),addedge(x,y);
for (T.DFS(1),T.init(),i=1;i<=m;++i)
{
F.read(x); F.read(y); int fa=T.getLCA(x,y);
SEG.modify(rt[x],x,1); SEG.modify(rt[x],y,1);
SEG.modify(rt[y],x,1); SEG.modify(rt[y],y,1);
tag[fa].pb(x); tag[fa].pb(y); tag[anc[fa]].pb(x); tag[anc[fa]].pb(y);
}
return DFS(1),printf("%lld",ans>>1LL),0;
}

Luogu P5327 [ZJOI2019]语言的更多相关文章

  1. 【题解】Luogu P5327 [ZJOI2019]语言

    原题传送门 看到这种树上统计点对个数的题一般是线段树合并,这题也不出意外 先对这棵树进行树剖,对于每次普及语言,在\(x,y\)两点的线段树上的\(x,y\)两位置打\(+1\)标记,在点\(fa[l ...

  2. 题解 P5327 [ZJOI2019]语言

    P5327 [ZJOI2019]语言 解题思路 暴力 首先讲一下我垃圾的 40pts 的暴力(其他 dalao 都是 60pts 起步): 当然评测机快的话(比如 LOJ 的),可以卡过 3,4 个点 ...

  3. P5327 [ZJOI2019]语言

    一边写草稿一边做题吧.要看题解的往下翻,或者是旁边的导航跳一下. 草稿 因为可以开展贸易活动的条件是存在一种通用语 \(L\) 满足 \(u_i\) 到 \(v_i\) 的最短路径上都会 \(L\) ...

  4. Luogu P5279 [ZJOI2019]麻将

    ZJOI2019神题,间接送我退役的神题233 考场上由于T2写挂去写爆搜的时候已经没多少时间了,所以就写挂了233 这里不多废话直接开始讲正解吧,我们把算法分成两部分 1.建一个"胡牌自动 ...

  5. (大模拟紫题) Luogu P1953 易语言

    原题链接:P1953 易语言 (我最近怎么总在做大模拟大搜索题) 分别处理两种情况. 如果只有一个1或0 直接设一个cnt为这个值,每次输入一个新名字之后把数字替换成cnt,最后cnt++即可. 注意 ...

  6. [Luogu5327][ZJOI2019]语言(树上差分+线段树合并)

    首先可以想到对每个点统计出所有经过它的链的并所包含的点数,然后可以直接得到答案.根据实现不同有下面几种方法.三个log:假如对每个点都存下经过它的链并S[x],那么每新加一条路径进来的时候,相当于在路 ...

  7. Luogu P5280 [ZJOI2019]线段树

    送我退役的神题,但不得不说是ZJOIDay1最可做的一题了 先说一下考场的ZZ想法以及出来后YY的优化版吧 首先发现每次操作其实就是统计出增加的节点个数(原来的不会消失) 所以我们只要统计出线段树上每 ...

  8. [ZJOI2019]语言

    树链剖分入门题吧 一个非常直观的想法是使用树剖将一条链拆成\(log^2n\)个矩形,套用矩形面积并算法即可得到一个垃圾的3个log过不去算法 为了得到一个两个log的做法,我们观察一下拆出来的矩形的 ...

  9. [ZJOI2019]语言[树链的并、线段树合并]

    题意 题目链接 分析 考虑枚举每个点的答案,最后除以 2 即可. 可以与 \(u\) 构成合法点对 的集合 为所有经过了 \(u\) 的链的并.因为这些链两两有交,根据结论 "树上两条相交的 ...

随机推荐

  1. window.location js截取url地址

    window.location方法的说明 原文链接: http://jiantian.org/index.php?page_id=2 window.location.href 整个URl字符串(在浏览 ...

  2. web开发菜鸟应该如何向前端大神提问题(一次性把问题描述清楚)

    1. 问题的环境和背景这里的背景一般包括,是针对桌面浏览器还是移动端开发?如果是桌面浏览器,则兼容性要求如何?比方说,你来咨询父级是百分比高度的垂直居中效果,你就要说明,你这个效果是需要兼容IE7+还 ...

  3. IT行业技术及程序员相关网站荟萃

    最近我花了一些时间收集了一些与自己工作相关的常用的网址,由于时间关系,暂时只是收集了这么多,以后有时间再随时添加. 1.程序员网址导航pg265 http://www.pg265.com/TNT程序网 ...

  4. Android开发实践:掌握Camera的预览方向和拍照方向

    http://ticktick.blog.51cto.com/823160/1592267?utm_source=tuicool&utm_medium=referral Android的Cam ...

  5. DMOJ IOI '17 P3 - Toy Train【拓扑排序】

    传送:https://dmoj.ca/problem/ioi17p3 参考:https://blog.csdn.net/qq_27327327/article/details/80711824 妙啊- ...

  6. JPA_day01

  7. python之内置函数(lambda,sorted,filter,map),递归,二分法

    一.lambda匿名函数 为了解决一些简单需求而设计的一句话函数,lambda表示的是匿名函数,不需要用def来声明,一句话就可以声明出一个函数. 语法: 函数名 = lambda 参数 : 返回值 ...

  8. <?php } ?> 标记

    只是为了分离php 和html 代码的一种书写方法. 你要知道 一段程序代码 function fool(){//内容}是这么组成的那么当有html代码的时候就需要先暂时将php的开始部分给分开(不分 ...

  9. Thinking In Java持有对象阅读记录

    这里记录下一些之前不太了解的知识点,还有一些小细节吧 序 首先,为什么要有Containers来持有对象,直接用array不好吗?——数组是固定大小的,使用不方便,而且是只能持有一个类型的对象,但当你 ...

  10. Unity Shader入门精要学习笔记 - 第17章 Unity的表面着色器探秘

    转自 冯乐乐的<Unity Shader 入门精要> 2010年的Unity 3 中,Surface Shader 出现了. 表面着色器的一个例子. 我们先做如下准备工作. 1)新建一个场 ...