BZOJ 4012 HNOI2015 开店 树的边分治+分治树
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=4012
题意概述:给出一颗N点的树,保证树上所有点的度不超过3,树上每个点有权值,每条边有权值,现在有Q组询问,每组给出信息u,L,R,问点权在区间[L,R]的点到点u的距离和为多少。强制在线。
N<=150000,Q<=200000.
可能这是我这几天做过的题里面最水但是最码的一个了。。。。
实际上看见树上所有点的度不超过3就感觉可以用边分治做,具体的思路实际上很简单,建立一个边分治结构,这个结构大约有logN层,每层有N个点,对于每组询问,看询问的点u在当前树被分成的两棵子树中的哪一棵,统计出另一个子树中所有点权在询问区间内的点到u的距离,然后递归到u所在的子树,最终就可以计算出答案。
重点在于实现,一不小心处理的时候就会多写出几个常数出来。。。(不要问我是怎么知道的)
实际上这个题我yy的东西重点就在于把边分治建立成一个二叉树的结构然后在里面去搞事情(小火车万岁!),最多有2N个分治结构(每条边会产生2个,有N-1条边)。每一层每个点只会有一个信息,利用这个性质可以记录很多东西,询问的时候在分治结构中递归,令当前分治结构中询问点所在的子树根节点为x,另一边的子树根节点为y(实际上就是断掉的边的两个端点),每一次询问的时候就在y的信息里面二分计算询问区间内点到y的距离,同时得到这些点的数量,然后把从y经过x到u的距离补全累加到答案中,递归。(要点已经交代完了,剩下的请各位自己yy,手动滑稽)
预处理可以用归并排序做到O(nlogn),主要复杂度在查询,单次是O(log2n)。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<set>
#include<map>
#include<vector>
#include<cctype>
using namespace std;
const int maxn=;
const int maxd=;
typedef long long LL; int N,Q,A,L,R,age[maxn]; LL ans,dist[maxd][maxn],sum[maxd][maxn];
struct edge{ int to,next,w; }E[maxn<<];
int first[maxn],np,sz[maxn],rt[maxd][maxn];
int ID[maxd][maxn],cnt;
int ke[maxn<<],MAX,ch[maxn<<][],l[maxn<<],r[maxn<<],size[maxd],tot[maxn<<];
bool vis[maxn<<]; void _scanf(int &x)
{
x=;
char cha=getchar();
while(cha<''||cha>'') cha=getchar();
while(cha>=''&&cha<='') x=x*+cha-'',cha=getchar();
}
int out_cnt,out[];
void _printf(LL x)
{
out_cnt=;
out[++out_cnt]=x%,x/=;
while(x) out[++out_cnt]=x%,x/=;
while(out_cnt) putchar(''+out[out_cnt--]);
}
void add_edge(int u,int v,int w)
{
E[++np]=(edge){v,first[u],w};
first[u]=np;
}
void data_in()
{
_scanf(N);_scanf(Q);_scanf(A);
for(int i=;i<=N;i++) _scanf(age[i]);
int x,y,z;
for(int i=;i<N;i++){
_scanf(x);_scanf(y);_scanf(z);
add_edge(x,y,z);
add_edge(y,x,z);
}
}
void DFS(int i,int f,int SZ,int id,int d,LL l)
{
sz[i]=,size[d]++,dist[d][i]=l;
for(int p=first[i];p;p=E[p].next){
int j=E[p].to;
if(j==f||vis[p]) continue;
rt[d][j]=rt[d][i];
DFS(j,i,SZ,id,d,l+E[p].w);
sz[i]+=sz[j];
int tmp=max(sz[j],SZ-sz[j]);
if(tmp<MAX) MAX=tmp,ke[id]=p;
}
}
void merge_sort(int x,int d)
{
int pos=l[x],i=l[ch[x][]],j=r[ch[x][]],r1=r[ch[x][]],r2=r[ch[x][]];
while(i<r1&&j<r2) ID[d][pos++]=age[ID[d+][i]]<age[ID[d+][j]]?ID[d+][i++]:ID[d+][j++];
while(i<r1) ID[d][pos++]=ID[d+][i++];
while(j<r2) ID[d][pos++]=ID[d+][j++];
}
void div_tree(int i,int SZ,int id,int d)
{
MAX=SZ;
l[id]=size[d],rt[d][i]=i;
DFS(i,,SZ,id,d,);
r[id]=size[d];
if((tot[id]=SZ)==){ ID[d][l[id]]=i; return; }
int o0=E[ke[id]].to,o1=E[(ke[id]-^)+].to;
int _sz0=sz[o0],_sz1=SZ-sz[o0];
vis[ke[id]]=vis[(ke[id]-^)+]=;
div_tree(o0,_sz0,ch[id][]=++cnt,d+);
div_tree(o1,_sz1,ch[id][]=++cnt,d+);
merge_sort(id,d);
sum[d][l[id]]=dist[d][ID[d][l[id]]];
for(int j=l[id]+;j<r[id];j++) sum[d][j]=sum[d][j-]+dist[d][ID[d][j]];
}
bool cmp(int x,int y){ return age[x]<age[y]; }
void solve(int p,int id,int d)
{
if(tot[id]==) return;
int x=E[ke[id]].to,y=E[(ke[id]-^)+].to;
int dd=rt[d][p]!=x,t=ch[id][dd^];
age[]=L;
int ll=lower_bound(ID[d]+l[t],ID[d]+r[t],,cmp)-ID[d]-;
LL v1=ll>=l[t]?sum[d][ll]:;
age[]=R;
int rr=upper_bound(ID[d]+l[t],ID[d]+r[t],,cmp)-ID[d]-;
LL v2=rr>=l[t]?sum[d][rr]:;
ans+=v2-v1+(rr-ll)*(E[ke[id]].w+dist[d][p]);
solve(p,ch[id][dd],d+);
}
void work()
{
div_tree(,N,++cnt,);
int u,a,b;
for(int i=;i<=Q;i++){
_scanf(u);_scanf(a);_scanf(b);
L=min((a+ans)%A,(b+ans)%A);
R=max((a+ans)%A,(b+ans)%A);
ans=;
solve(u,,);
_printf(ans),putchar('\n');
}
}
int main()
{
data_in();
work();
return ;
}
BZOJ 4012 HNOI2015 开店 树的边分治+分治树的更多相关文章
- BZOJ 4012 [HNOI2015]开店 (区间修改 永久化标记 主席树)
讲得好啊 主席树区间修改了,每一次遇到整区间就打永久化标记(不下传,访问的时候沿路径上的标记算答案)然后returnreturnreturn,那么每修改一次只会访问到lognlognlogn个节点,再 ...
- BZOJ 4012 [HNOI2015]开店 (树分治+二分)
题目大意: 给你一棵树,边有边权,点有点权,有很多次询问,求点权$\in[l,r]$的所有节点到某点$x$的距离之和,强制在线 感觉这个题应该放在动态点分之前做= = 套路方法和动态点分是一样的 每次 ...
- bzoj 4012: [HNOI2015]开店 主席树
Description 风见幽香有一个好朋友叫八云紫,她们经常一起看星星看月亮从诗词歌赋谈到 人生哲学.最近她们灵机一动,打算在幻想乡开一家小店来做生意赚点钱.这样的 想法当然非常好啦,但是她们也发现 ...
- bzoj 4012: [HNOI2015]开店
Description 风见幽香有一个好朋友叫八云紫,她们经常一起看星星看月亮从诗词歌赋谈到 人生哲学.最近她们灵机一动,打算在幻想乡开一家小店来做生意赚点钱.这样的 想法当然非常好啦,但是她们也发现 ...
- 【BZOJ】4012: [HNOI2015]开店
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=4012 给出一个$n$个点的树,树上每一个点都有一个值$age$,每条边都有边权,每次查询一 ...
- [BZOJ4012][HNOI2015]开店(动态点分治,树链剖分)
4012: [HNOI2015]开店 Time Limit: 70 Sec Memory Limit: 512 MBSubmit: 2168 Solved: 947[Submit][Status] ...
- BZOJ4012[HNOI2015]开店——树链剖分+可持久化线段树/动态点分治+vector
题目描述 风见幽香有一个好朋友叫八云紫,她们经常一起看星星看月亮从诗词歌赋谈到 人生哲学.最近她们灵机一动,打算在幻想乡开一家小店来做生意赚点钱.这样的 想法当然非常好啦,但是她们也发现她们面临着一个 ...
- 【BZOJ4012】[HNOI2015]开店 动态树分治+二分
[BZOJ4012][HNOI2015]开店 Description 风见幽香有一个好朋友叫八云紫,她们经常一起看星星看月亮从诗词歌赋谈到人生哲学.最近她们灵机一动,打算在幻想乡开一家小店来做生意赚点 ...
- [HNOI2015]开店 树链剖分,主席树
[HNOI2015]开店 LG传送门 蒟蒻表示不会动态淀粉质. 先把点按年龄排序, 设\(dis[i]\)表示\(i\)到根的距离. 把我们要算的东西稍微变下形:\(ans\) \[ = \sum \ ...
随机推荐
- Python 学习笔记(七)Python字符串(一)
字符串 字符串或串(String)是由数字.字母.下划线组成的一串字符,用双引号或单引号包裹的为字符串 1 >>> "hello world" 2 'hello ...
- 菜鸟笔记 -- Chapter 6.4.3 多态
6.4.3 多态 多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方 ...
- UML绘制活动图--客户来电咨询活动图
选择Logic View–>New–>Activity Diagram 修改NewActivity为客户来电咨询 选择初始状态和终止状态(下图中上面是Start State,下面是End ...
- C.Sum 2017 ACM-ICPC 亚洲区(西安赛区)网络赛
题目来源:Sum 限制:1000ms 32768K Define the function S(x) for xx is a positive integer. S(x) equals to the ...
- PHP+Mysql注入防护与绕过
今天给大家分享一个关于php常见的注入防护以及如何bypass的文章,文章内容来源国外某大佬总结,我做了一下整理,文章来源地址不详,下面正文开始.以下的方式也仅仅是针对黑名单的过滤有一定的效果,为了安 ...
- LVS、keepalived原理及配置
使用LVS实现负载均衡原理及安装配置详解 负载均衡集群是 load balance 集群的简写,翻译成中文就是负载均衡集群.常用的负载均衡开源软件有nginx.lvs.haproxy,商业的硬件负 ...
- Python练习笔记(2)
文件读写,多线程.多进程 import time,os,threading,random def file_read(path): try: with open(path, 'r') as f: # ...
- Fabric go sdk初始化所需证书解析
fabric sdk go 提供的官方文档少之又少,要想入门,主要就靠研究官方的e2e系列示例,这真的是一件挺无奈的事情.没法子,只能硬着头皮上了.研究发现,e2e这个例子是通过cryptogen生成 ...
- Java——自动生成30道四则运算---18.09.27
package chuti;import java.io.PrintWriter;import java.util.Scanner;import java.io.FileNotFoundExcepti ...
- centos配置npm全局安装
使用-g全局安装在服务器需要配置,下面看看配置方法 配置全局安装路径和缓存路径 cd /usr/local/nodejs mkdir node_global mkdir node_cache npm ...