原文链接www.cnblogs.com/zhouzhendong/p/UOJ435.html

前言

分块题果然是我这种蒟蒻写不动的。由于种种原因,我写代码的时候打错了很多东西,最致命的是数组开小了。**windows不能检测数组越界,能眼查出来这运气是真的好。

题解

首先树链剖分,把问题转化为序列上的问题。

然后我们分块。

考虑如何维护每一块的答案。

初始时,我们预处理将每一块的相同值元素合并。

考虑修改操作:

如果是整块修改,那么临界指针位移即可。

否则就是零散修改,我们可以直接重构整个块。由于时间复杂度要求比较紧,所以我们需要想一个不带 log 的重构方法。

首先一个棘手的问题就是我们难以在不带 log 的前提下对要修改的值在块内有序序列中的定位。

于是我们考虑再维护一个不离散化的有序表,并提前处理出每一个值在这个有序表中的位置。这样,我们在修改了一些值之后,只需要做一次对两个有序表进行归并即可。

(写完代码后才发现似乎可以简单些……)

由于套了一层树链剖分,所以看起来时间复杂度是 $O(m\sqrt{n}\log n)$ 的。

但是仔细看可以发现,单次操作中,整块修改的次数是 $O(\sqrt n)$ 的,块内重构的次数是 $O(\sqrt n\log n)$ 的。

于是,通过调整块大小,就可以得到了一个 $O(n\sqrt{n \log n})$ 的算法。

代码

#include <bits/stdc++.h>
#define clr(x) memset(x,0,sizeof (x))
#define For(i,a,b) for (int i=a;i<=b;i++)
#define Fod(i,b,a) for (int i=b;i>=a;i--)
#define pb(x) push_back(x)
using namespace std;
typedef long long LL;
LL read(){
LL x=0,f=0;
char ch=getchar();
while (!isdigit(ch))
f|=ch=='-',ch=getchar();
while (isdigit(ch))
x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
return f?-x:x;
}
const int N=100005;
int n,m,type;
int a[N];
vector <int> e[N];
int fa[N],depth[N],size[N],son[N],top[N],I[N],O[N],aI[N];
void dfs1(int x,int pre,int d){
fa[x]=pre,depth[x]=d,size[x]=1,son[x]=0;
for (auto y : e[x])
if (y!=pre){
dfs1(y,x,d+1);
size[x]+=size[y];
if (!son[x]||size[y]>size[son[x]])
son[x]=y;
}
}
int Time=0;
void dfs2(int x,int Top){
I[x]=++Time;
aI[Time]=x;
top[x]=Top;
if (son[x])
dfs2(son[x],Top);
for (auto y : e[x])
if (y!=fa[x]&&y!=son[x])
dfs2(y,y);
O[x]=Time;
}
int lastans=0;
const int B=50;
int v[N/B+5][B+5],c[N/B+5][B+5],v2[N/B+5][B+5];
int cnt[N/B+5],d[N/B+5],p[N/B+5],tot[N/B+5],cnt2[N/B+5];
int pos[N];
int bid(int x){
return x==0?-1:(x-1)/B;
}
void getv(int b){
v[b][1]=a[v2[b][1]];
c[b][1]=cnt[b]=1;
For(i,2,cnt2[b]){
if (a[v2[b][i]]==a[v2[b][i-1]])
c[b][cnt[b]]++;
else {
c[b][++cnt[b]]=1;
v[b][cnt[b]]=a[v2[b][i]];
}
}
tot[b]=d[b]=0,p[b]=cnt[b]+1;
while (p[b]>1&&v[b][p[b]-1]>0)
tot[b]+=c[b][--p[b]];
}
void rebuild(int b,int L,int R,int v){
static int f[B+5],tmp[B+5],id[B+5];
For(i,1,cnt2[b])
a[v2[b][i]]+=d[b];
d[b]=0,clr(f),clr(tmp);
For(i,L,R){
int x=aI[i],ps=pos[x];
f[ps]=1,tmp[ps]=x,a[x]+=v;
}
int idc=0,i=1,j=1;
while (1){
while (i<=cnt2[b]&&f[i])
i++;
while (j<=cnt2[b]&&!f[j])
j++;
if (i>cnt2[b]&&j>cnt2[b])
break;
if (i<=cnt2[b]&&(j>cnt2[b]||a[v2[b][i]]<a[tmp[j]]))
id[++idc]=v2[b][i++];
else
id[++idc]=tmp[j++];
}
cnt2[b]=idc;
For(i,1,idc)
v2[b][i]=id[i];
For(i,1,cnt2[b])
pos[v2[b][i]]=i;
getv(b);
}
void Upd(int L,int R,int w){
int Lid=bid(L),Rid=bid(R);
if (Lid==Rid)
rebuild(Lid,L,R,w);
else {
rebuild(Lid,L,(Lid+1)*B,w);
rebuild(Rid,Rid*B+1,R,w);
For(i,Lid+1,Rid-1){
d[i]+=w;
while (p[i]<=cnt[i]&&v[i][p[i]]+d[i]<=0)
tot[i]-=c[i][p[i]++];
while (p[i]>1&&v[i][p[i]-1]+d[i]>0)
tot[i]+=c[i][--p[i]];
}
}
}
bool cmp(int x,int y){
return a[x]<a[y];
}
void update(int x,int y,int v){
int fx=top[x],fy=top[y];
while (fx!=fy){
if (depth[fx]<depth[fy])
swap(fx,fy),swap(x,y);
Upd(I[fx],I[x],v);
x=fa[fx],fx=top[x];
}
if (depth[x]>depth[y])
swap(x,y);
Upd(I[x],I[y],v);
}
int Query_block(int L,int R){
int ans=0,Lid=bid(L),Rid=bid(R);
if (Lid==Rid){
For(i,L,R)
ans+=a[aI[i]]+d[Lid]>0;
}
else {
Fod(i,(Lid+1)*B,L)
ans+=a[aI[i]]+d[Lid]>0;
For(i,Rid*B+1,R)
ans+=a[aI[i]]+d[Rid]>0;
For(i,Lid+1,Rid-1)
ans+=tot[i];
}
return ans;
}
int Query(int x,int y){
int ans=0,fx=top[x],fy=top[y];
while (fx!=fy){
if (depth[fx]<depth[fy])
swap(fx,fy),swap(x,y);
ans+=Query_block(I[fx],I[x]);
x=fa[fx],fx=top[x];
}
if (depth[x]>depth[y])
swap(x,y);
ans+=Query_block(I[x],I[y]);
return ans;
}
int main(){
n=read(),m=read(),type=read();
For(i,1,n-1){
int x=read(),y=read();
e[x].pb(y),e[y].pb(x);
}
For(i,1,n)
a[i]=read();
dfs1(1,0,0);
dfs2(1,1);
For(i,1,n){
int b=bid(I[i]);
v2[b][++cnt2[b]]=i;
}
int mxb=bid(n);
For(i,0,mxb){
sort(v2[i]+1,v2[i]+cnt2[i]+1,cmp);
For(j,1,cnt2[i])
pos[v2[i][j]]=j;
getv(i);
}
while (m--){
int op=read();
if (op==1){
int x=read(),y=read(),w=read();
if (type)
x^=lastans,y^=lastans;
update(x,y,w);
}
else if (op==2){
int x=read(),y=read();
if (type)
x^=lastans,y^=lastans;
printf("%d\n",lastans=Query(x,y));
}
else if (op==3){
int x=read();
if (type)
x^=lastans;
printf("%d\n",lastans=Query_block(I[x],O[x]));
}
}
return 0;
}

  

UOJ#435. 【集训队作业2018】Simple Tree 树链剖分,分块的更多相关文章

  1. Hdu 5274 Dylans loves tree (树链剖分模板)

    Hdu 5274 Dylans loves tree (树链剖分模板) 题目传送门 #include <queue> #include <cmath> #include < ...

  2. POJ3237 Tree 树链剖分 边权

    POJ3237 Tree 树链剖分 边权 传送门:http://poj.org/problem?id=3237 题意: n个点的,n-1条边 修改单边边权 将a->b的边权取反 查询a-> ...

  3. Query on a tree——树链剖分整理

    树链剖分整理 树链剖分就是把树拆成一系列链,然后用数据结构对链进行维护. 通常的剖分方法是轻重链剖分,所谓轻重链就是对于节点u的所有子结点v,size[v]最大的v与u的边是重边,其它边是轻边,其中s ...

  4. 【BZOJ-4353】Play with tree 树链剖分

    4353: Play with tree Time Limit: 20 Sec  Memory Limit: 256 MBSubmit: 31  Solved: 19[Submit][Status][ ...

  5. SPOJ Query on a tree 树链剖分 水题

    You are given a tree (an acyclic undirected connected graph) with N nodes, and edges numbered 1, 2, ...

  6. poj 3237 Tree 树链剖分

    题目链接:http://poj.org/problem?id=3237 You are given a tree with N nodes. The tree’s nodes are numbered ...

  7. Codeforces Round #200 (Div. 1) D Water Tree 树链剖分 or dfs序

    Water Tree 给出一棵树,有三种操作: 1 x:把以x为子树的节点全部置为1 2 x:把x以及他的所有祖先全部置为0 3 x:询问节点x的值 分析: 昨晚看完题,马上想到直接树链剖分,在记录时 ...

  8. poj 3237 Tree 树链剖分+线段树

    Description You are given a tree with N nodes. The tree’s nodes are numbered 1 through N and its edg ...

  9. Aizu 2450 Do use segment tree 树链剖分+线段树

    Do use segment tree Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://www.bnuoj.com/v3/problem_show ...

随机推荐

  1. Security+ 认证考过经验分享 802分飘过

    PART 1/考前准备 1.针对与新人.学生建议看每一节直播课程,老师会结合自己的工作工作经验讲解课程,可以帮助学生理解知识. 2.备考期间建议官方指导手册至少看两遍以上,我在结合自己的做题库时发现有 ...

  2. Python菜鸟快乐游戏编程_pygame(1)

    Python菜鸟快乐游戏编程_pygame(博主录制,2K分辨率,超高清) https://study.163.com/course/courseMain.htm?courseId=100618802 ...

  3. MVC实例应用模式

    MVC实例应用模式 1.可用性: 比如异常处理 2.可修改性: 比如用接口实现 3.性能战术: 4.易用性战术: 分层实现 5.可测试性战术: 实现对其接口进行测试,并不需要对其实现方法进行 6.安全 ...

  4. C#使用Selenium+PhantomJS抓取数据

    本文主要介绍了C#使用Selenium+PhantomJS抓取数据的方法步骤,具有很好的参考价值,下面跟着小编一起来看下吧 手头项目需要抓取一个用js渲染出来的网站中的数据.使用常用的httpclie ...

  5. vue-router.esm.js:1905 TypeError: Cannot convert undefined or null to object

    环境:vue+vuex+element 报错原因 ...mapState('isDialNumber') mapState如果传递一个参数,参数必须是数组 ...mapState(['isDialNu ...

  6. 中国 A 股纳入 MSCI

    1 .什么是 MSCI MSCI 是美国指数编制公司 --- 美国明晟公司的简称. 是一家股权,固定资产,对冲基金,股票市场指数的供应商. MSCI 旗下编制了多种指数,他们把全球股票市场分成发达国家 ...

  7. Asp.net Core导出Excel

    本篇文章是在MVC设计模式下,基于windows系统的Excel导出 1.前台的实现不用我多说了吧,加一个a标签链接地址跳到它所调用的方法里面,可以根据当前页面的查询条件去传值,从而查询出你想要的数据 ...

  8. EXCEL上传POI

    Java SpringMVC POI上传excel并读取文件内容 2017年11月27日 15:26:56 强人锁男. 阅读数:15329   用的SSM框架,所需要的jar包如图所示:,链接地址:j ...

  9. 第十五节、OpenCV学习(四)图像平滑与滤波

    图像的平滑与滤波 平滑滤波是低频增强的空间域滤波技术,是图像模糊.消除噪声. 一.2D滤波器cv2.filter2D() 对于2D图像可以进行低通或者高通滤波操作,低通滤波(LPF)有利于去噪声,模糊 ...

  10. 基于Python的Webservice开发(三)-Django安装配置

    一.安装Django pip install django 二.创建项目 进入指定的目录后 django-admin startproject WebApi 目录说明: WebApi 项目的容器. m ...