树链剖分吼啊

一看就看出是LCT模板题啦

前记

见这么多人写LCT,却很少人写树链剖分,于是我就来一发树链剖分(其实是因为自己不会LCT)

本蒟蒻的写法和诸位写树链剖分的大神有点不同

思路

树链剖分,简单题

操作

操作1:'C' 操作 : 简单的说就是把x到y的边cut掉,题目又保证x,y相邻,肯定直接LCT啦,那么我们就可以将x,y的所以边权加1,表示那些边多了一次战争。

操作2:'U' 操作 : 简单的说就是把之前cut的边加回来,我们只需要记录之前每一次战争的两个部落编号,战争结束就把那两个部落直接的边权减去1就可以了,表示那些边少了一次战争。

操作3:'Q' 操作 : 简单的说就是询问x,y之间的边权和,如果是0,表示经过的边一次战争也没有,就是yes;不然就有战争,是no。

这么一看,思路是不是很简单。

但还有些问题,树链剖分是针对点权的,我们如何转换为边权呢?

点权转边权

方法有很多,例如在每一条边都多加1个点,在加的那个点上记录权值。

但是,这里讲一下我的做法:直接忽略

在一颗树内,点有n个,边有n-1条,所以我们可以让每一条边对应到点上,对应方式就是边的编号是边上两点深度小的点编号。

那么就只会有根是没有边对应的所以我们可以假设有一条编号为根的边和根相连。

将x,y的边权加就等于将x,y的点权全部加,然后LCA(x,y)的点权减回去(可以自己画图尝试一下)

查询x,y的边权和就等于将x,y的点权全部累加,然后减去LCA(x,y)的点权(可以自己画图尝试一下)

于是我们整个思路就出来了(这样就不用改线段树啦>w< )

代码

#include<bits/stdc++.h>
#define maxn 4000001
#define L(x) (x<<1)
#define R(x) ((x<<1)|1)
using namespace std;
int tree[maxn],tag[maxn];
int rev[maxn],dep[maxn],size[maxn],seg[maxn],top[maxn],son[maxn],father[maxn];
int n,m,root,x,y,z,a[maxn],visx[maxn],visy[maxn],tot;
int cnt,from[maxn],to[maxn],Next[maxn],head[maxn];
char mode;
void add(int x,int y){
cnt++;
from[cnt]=x;to[cnt]=y;
Next[cnt]=head[x];head[x]=cnt;
}
//线段树
void pushdown(int node,int begin,int end){
if(tag[node]){
tag[L(node)]+=tag[node];
tag[R(node)]+=tag[node];
int mid=(begin+end)>>1;
tree[L(node)]+=(mid-begin+1)*tag[node];
tree[R(node)]+=(end-mid)*tag[node];
tag[node]=0;
}
}
void update(int node,int begin,int end,int x,int y,int val){
if(begin>y||end<x)return;
if(begin>=x&&end<=y){
tag[node]+=val;
tree[node]+=(end-begin+1)*val;
return;
}else{
pushdown(node,begin,end);
int mid=(begin+end)>>1;
if(x<=mid)update(L(node),begin,mid,x,y,val);
if(y>mid) update(R(node),mid+1,end,x,y,val);
tree[node]=tree[L(node)]+tree[R(node)];
}
}
int query(int node,int begin,int end,int x,int y){
if(begin>y||end<x)return 0;
if(begin>=x&&end<=y){
return tree[node];
}else{
pushdown(node,begin,end);
int mid=(begin+end)>>1,sum=0;
if(x<=mid)sum+=query(L(node),begin,mid,x,y);
if(y>mid) sum+=query(R(node),mid+1,end,x,y);
return sum;
}
}
//线段树
int dfs1(int x){ //树链剖分模板
size[x]=1;
dep[x]=dep[father[x]]+1;
for(int i=head[x];i!=-1;i=Next[i]){
int v=to[i],big=0;
if(father[x]==v)continue;
father[v]=x;
big=dfs1(v);
size[x]+=big;
if(big>size[son[x]])son[x]=v;
}
return size[x];
}
void dfs2(int x){ //树链剖分模板
if(son[x]){
seg[son[x]]=++seg[0];
top[son[x]]=top[x];
rev[seg[0]]=son[x];
dfs2(son[x]);
}
for(int i=head[x];i!=-1;i=Next[i]){
int v=to[i];
if(!top[v]){
seg[v]=++seg[0];
top[v]=v;
rev[seg[0]]=v;
dfs2(v);
}
}
}
void linkadd(int x,int y,int z){
int fx=top[x],fy=top[y];
while(fx!=fy){
if(dep[fx]<dep[fy])swap(x,y),swap(fx,fy);
update(1,1,seg[0],seg[fx],seg[x],z);
x=father[fx];fx=top[x];
}
if(dep[x]>dep[y])swap(x,y);
update(1,1,seg[0],seg[x],seg[y],z);
update(1,1,seg[0],seg[x],seg[x],-z); //LCA特殊处理
}
int linkquery(int x,int y){
int fx=top[x],fy=top[y],ans=0;
while(fx!=fy){
if(dep[fx]<dep[fy])swap(x,y),swap(fx,fy);
ans+=query(1,1,seg[0],seg[fx],seg[x]);
x=father[fx];fx=top[x];
}
if(dep[x]>dep[y])swap(x,y);
ans+=query(1,1,seg[0],seg[x],seg[y]);
ans-=query(1,1,seg[0],seg[x],seg[x]); //减LCA
return ans;
}
int main(){
memset(head,-1,sizeof(head));
scanf("%d%d",&n,&m);root=1;
for(int i=1;i<=n-1;i++){
scanf("%d%d",&x,&y);
add(x,y);add(y,x);
}
dfs1(root);
seg[root]=++seg[0];
rev[seg[0]]=root;
top[root]=root;
dfs2(root);
for(int i=1;i<=m;i++){
scanf("%s",&mode);
if(mode=='C'){
scanf("%d%d",&x,&y);
visx[++tot]=x;visy[tot]=y; //记录每一次战争的两个部落
linkadd(x,y,1); //x到y的边权加1
}
if(mode=='U'){
scanf("%d",&x);
linkadd(visx[x],visy[x],-1); //战争结束就减回去
}
if(mode=='Q'){
scanf("%d%d",&x,&y);
int q=linkquery(x,y); //查询x到y的边权和
if(q==0)printf("Yes\n");else //如果q为0就可以
printf("No\n"); //不行就……
}
}
}

另外推荐题目

P3258 [JLOI2014]松鼠的新家 即其题解

题解 P3950 【部落冲突】的更多相关文章

  1. luogu题解 P3950部落冲突--树链剖分

    题目链接 https://www.luogu.org/problemnew/show/P3950 分析 大佬都用LCT,我太弱只会树链剖分 一个很裸的维护边权树链剖分题.按照套路,对于一条边\(< ...

  2. lupgu P3950 部落冲突

    题目链接 luogu P3950 部落冲突 题解 树剖线段树可以 lct还行 代码 #include<cstdio> #include<algorithm> inline in ...

  3. 洛谷 P3950 部落冲突 树链剖分

    目录 题面 题目链接 题目描述 输入输出格式 输入格式 输出格式 输入输出样例 输入样例1 输出样例1 输入样例2 输出样例2 输入样例3 输出样例3 说明 思路 AC代码 总结 题面 题目链接 P3 ...

  4. 洛谷P3950 部落冲突 [LCT]

    题目传送门 部落冲突 格式难调,体面就不放了. 分析: julao们应该都看得出来就是个$LCT$板子,战争就$cut$,结束就$link$,询问就$find$.没了... 太久没打$LCT$,然后发 ...

  5. 【luogu P3950 部落冲突】 题解

    题目连接:https://www.luogu.org/problemnew/show/P3950 1.像我这种学数据结构学傻了的 2.边权化点权 所有点权初始化0 3.对于战争 将深度较深的-1,对于 ...

  6. 【题解】Luogu P3950 部落冲突

    原题传送门 这题用Link-Cut-Tree解决,Link-Cut-Tree详解 我们用Link-Cut-Tree维护连通性(十分无脑) 一开始先把树中每条边的两端连接 U操作:把u,v两个点连起来 ...

  7. [题解] 洛谷P3950 部落冲突

    传送门 拿到题目,一看 裸LCT (其实是我懒得打,splay又臭又长) 首先,这道题的意思就是删掉一些边 所以常规操作 点权转边权 之后对于战争操作,在对应的边上+1 对于和平操作,在对应的边上-1 ...

  8. 【刷题】洛谷 P3950 部落冲突

    题目背景 在一个叫做Travian的世界里,生活着各个大大小小的部落.其中最为强大的是罗马.高卢和日耳曼.他们之间为了争夺资源和土地,进行了无数次的战斗.期间诞生了众多家喻户晓的英雄人物,也留下了许多 ...

  9. P3950 部落冲突

    题目背景 在一个叫做Travian的世界里,生活着各个大大小小的部落.其中最为强大的是罗马.高卢和日耳曼.他们之间为了争夺资源和土地,进行了无数次的战斗.期间诞生了众多家喻户晓的英雄人物,也留下了许多 ...

  10. 洛谷P3950 部落冲突(LCT)

    洛谷题目传送门 最无脑LCT题解,Dalao们的各种算法都比这个好多啦... 唯一的好处就是只管码代码就好了 开战cut,停战link,询问findroot判连通性 太无脑,应该不用打注释了.常数大就 ...

随机推荐

  1. HDFS的常用命令

    一.常用命令 二.其他命令

  2. Plastic Sprayers Manufacturer - The Basic Components Of A Spray Bottle

    From cleaning to personal beauty, many people use spray bottles every day, but few people know how t ...

  3. 二叉树性质 n0=n2+1

    假设树的节点个数为n,那么n=n0+n1+n2,并且边的个数等于n-1,那么 n-1=n22+n1 则 n0+n1+n2-1=n22+n1,即n0=n2+1.

  4. linux的数据盘挂载

    图文教程: Linux的云服务器数据盘未做分区和格式化,可以根据以下步骤进行分区以及格式化操作. 一:登陆 用Linux 的SSH 登陆软件(xshell 或者putty) 登陆阿里云主机服务器. 二 ...

  5. 安装Ubuntu后的一些配置

    Ubuntu安装的一些配置 搜狗拼音的安装 卸载ibus和它的配置, 卸载顶部面板的键盘指示 sudo apt remove ibus sudo apt purge ibus sudo apt rem ...

  6. Java IO流详解(一)——简单介绍

    文件在程序中是以流的形式来传输的.所以用Java来传输文件就得使用到Java IO流. 1.流的概念和作用 流:代表任何有能力产出数据的数据源对象或者是有能力接受数据的接收端对象<Thinkin ...

  7. 20200213springboot日记

    ------------恢复内容开始------------ ------------恢复内容开始------------ ------------恢复内容开始------------ 数据库管理 L ...

  8. FTP文件传输服务!

    一.FTP  连接及传输模式 1.控制连接:TCP 21,用于发送 FTP 命令信息2.数据连接:TCP 20,用于上传.下载数据3.数据连接的建立类型: (1)主动模式:服务器主动发起数据连接 (2 ...

  9. 如何确认 fastboot unlock 解锁成功,如何确认DM-verity 已关闭

    如何确认 fastboot unlock 解锁成功 1.fastboot 模式下按音量上键后是否提示 Unlock Pass...return to fastboot in 3s 2.重启后界面是否显 ...

  10. Dart语言学习(十五) Dart函数方法

    Dart函数方法可分为两类: 一.内置方法/函数: print(); 二.自定义方法: 自定义方法的基本格式: 返回类型 方法名称(参数1,参数2,...){ 方法体 return 返回值; } vo ...