BZOJ4012[HNOI2015]开店——树链剖分+可持久化线段树/动态点分治+vector
题目描述
风见幽香有一个好朋友叫八云紫,她们经常一起看星星看月亮从诗词歌赋谈到
输入
第一行三个用空格分开的数 n、Q和A,表示树的大小、开店的方案个数和妖
输出
对于每个方案,输出一行表示方便值。
样例输入
0 0 7 2 1 4 7 7 7 9
1 2 270
2 3 217
1 4 326
2 5 361
4 6 116
3 7 38
1 8 800
6 9 210
7 10 278
8 9 8
2 8 0
9 3 1
8 0 8
4 2 7
9 7 3
4 7 0
2 2 7
3 2 1
2 3 4
样例输出
957
7161
9466
3232
5223
1879
1669
1282
0
提示
满足 n<=150000,Q<=200000。对于所有数据,满足 A<=10^9
树链剖分+可持久化线段树
这道题和BZOJ3626比较相似.首先考虑没有年龄限制,就是求一些点和一个点x的距离和.答案就是Σdep[i]+dep[x]-2*dep[lca],dep[i]可以处理前缀和,再求出点数乘上dep[x]就就得到了前半部分答案,最后lca深度和怎么求?如果单独求两点x,y的lca深度可以将x到根路径上所有边权值+1(每条边权值相同情况下)然后再求y到根路径上边权和就好了。那么一些点和x的lca深度和也可以用同样的求法,按树剖序建线段树,边权下传到点上,每次跳重链修改和查询。因为这道题边权不同,所以每次给边权+1表示这条边对答案贡献次数+1,维护一个永久化的标记(就是不下传的标记)表示区间要对答案贡献几次,再每个点维护一个贡献和表示这个点代表的区间对答案的总贡献(即这个点在线段树中子树中所有点的永久化标记*区间权值和之和)就行了。那么现在有了年龄限制显然一棵线段树是不行的,因此要把年龄离散化后从小到大每种年龄建一棵可持久化线段树,求年龄区间只要把对应可持久化线段树相减剩下的就只是对应年龄区间的点到根的标记。注意要开longlong。
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
ll ans;
int n,m;
int tot;
int cnt;
int num;
int A,k;
int l,r;
int x,y,z;
int p[150010];
int h[150010];
int f[150010];
int g[150010];
int q[150010];
int v[150010];
int to[300010];
ll s[20000010];
ll sum[150010];
int dep[150010];
int t[20000010];
int son[150010];
int top[150010];
int val[300010];
int head[150010];
int size[150010];
int next[300010];
int root[150010];
int ls[20000010];
int rs[20000010];
ll total[150010];
struct node
{
int x;
int id;
}a[150010];
bool cmp(node a,node b)
{
return a.x<b.x;
}
void add(int x,int y,int z)
{
tot++;
next[tot]=head[x];
head[x]=tot;
to[tot]=y;
val[tot]=z;
}
void dfs(int x)
{
size[x]=1;
for(int i=head[x];i;i=next[i])
{
if(to[i]!=f[x])
{
dep[to[i]]=dep[x]+val[i];
f[to[i]]=x;
v[to[i]]=val[i];
dfs(to[i]);
size[x]+=size[to[i]];
if(size[to[i]]>size[son[x]])
{
son[x]=to[i];
}
}
}
}
void dfs2(int x,int tp)
{
top[x]=tp;
p[x]=++num;
q[num]=x;
if(son[x])
{
dfs2(son[x],tp);
}
for(int i=head[x];i;i=next[i])
{
if(to[i]!=f[x]&&to[i]!=son[x])
{
dfs2(to[i],to[i]);
}
}
}
void pushup(int rt,int l,int r)
{
s[rt]=s[ls[rt]]+s[rs[rt]]+t[rt]*(sum[r]-sum[l-1]);
}
void change(int &rt,int pre,int l,int r,int L,int R)
{
rt=++cnt;
ls[rt]=ls[pre];
rs[rt]=rs[pre];
s[rt]=s[pre];
t[rt]=t[pre];
if(L<=l&&r<=R)
{
t[rt]++;
s[rt]+=sum[r]-sum[l-1];
return ;
}
int mid=(l+r)>>1;
if(L<=mid)
{
change(ls[rt],ls[pre],l,mid,L,R);
}
if(R>mid)
{
change(rs[rt],rs[pre],mid+1,r,L,R);
}
pushup(rt,l,r);
}
ll query(int rt,int l,int r,int L,int R)
{
if(L<=l&&r<=R)
{
return s[rt];
}
ll res=1ll*t[rt]*(sum[min(r,R)]-sum[max(l,L)-1]);
int mid=(l+r)>>1;
if(L<=mid)
{
res+=query(ls[rt],l,mid,L,R);
}
if(R>mid)
{
res+=query(rs[rt],mid+1,r,L,R);
}
return res;
}
void updata(int x,int rt)
{
while(top[x]!=1)
{
change(root[rt],root[rt],1,n,p[top[x]],p[x]);
x=f[top[x]];
}
change(root[rt],root[rt],1,n,1,p[x]);
}
ll find(int x,int l,int r)
{
ll res=0;
while(top[x]!=1)
{
res+=query(root[r],1,n,p[top[x]],p[x])-query(root[l],1,n,p[top[x]],p[x]);
x=f[top[x]];
}
res+=query(root[r],1,n,1,p[x])-query(root[l],1,n,1,p[x]);
return res;
}
int main()
{
scanf("%d%d%d",&n,&m,&A);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i].x);
h[i]=a[i].x;
a[i].id=i;
}
sort(h+1,h+n+1);
k=unique(h+1,h+1+n)-h-1;
for(int i=1;i<n;i++)
{
scanf("%d%d%d",&x,&y,&z);
add(x,y,z);
add(y,x,z);
}
dfs(1);
dfs2(1,1);
sort(a+1,a+1+n,cmp);
for(int i=1;i<=n;i++)
{
sum[i]=sum[i-1]+v[q[i]];
}
for(int i=1;i<=n;i++)
{
int fx=lower_bound(h+1,h+1+k,a[i].x)-h;
g[fx]=i;
if(fx==(lower_bound(h+1,h+1+k,a[i-1].x)-h))
{
total[fx]=total[fx]+1ll*dep[a[i].id];
}
else
{
total[fx]=total[fx-1]+1ll*dep[a[i].id];
root[fx]=root[fx-1];
}
updata(a[i].id,fx);
}
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&z,&x,&y);
l=min((x+ans)%A,(y+ans)%A);
r=max((x+ans)%A,(y+ans)%A);
l=upper_bound(h+1,h+1+k,l-1)-h-1;
r=upper_bound(h+1,h+1+k,r)-h-1;
ans=total[r]-total[l]+1ll*(g[r]-g[l])*dep[z]-2*find(z,l,r);
printf("%lld\n",ans);
}
}
动态点分治+vector
这道题用动态点分治做思路就很简单了,考虑对于单次询问如何用点分治解决。假设查询点为x,对于分治联通块中包含x的分治中心,统计联通块内点权在[L,R]之间的点到分治中心的距离和及点数,这些点到x的距离就是他们先到分治中心的距离+分治中心到x的距离。但与x位于分治中心同一子树中的点不能这样算,所以要容斥减掉这一部分的答案,也就是还要统计与x位于同一子树的这个联通块中所有点到分治中心的距离及点数。那么转换到点分树上就需要在每个点维护这个点在点分树上的子树中各个点权的点到这个点的距离和及点数与这个点在点分树上的子树中各个点权的点到这个点的父节点的距离和及点数。查询时同样往根爬容斥一下即可。对于维护这两种信息大家可能会想用线段树,但线段树的内存这道题显然开不下,不过可以发现所有询问都是在添加信息完成之后再处理,且对信息没有修改,所以可以每个点开两个vector存信息然后再排序。查询时直接在vector上二分查找即可。注意在vector两端开哨兵节点防止越界。
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<cmath>
#include<vector>
#include<bitset>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
#define pr pair<int,ll>
using namespace std;
inline char _read()
{
static char buf[100000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int read()
{
int x=0,f=1;char ch=_read();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=_read();}
while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=_read();}
return x*f;
}
ll ans;
int l,r;
int a,b;
int tot;
int dfn;
int num;
int cnt;
int rot;
int n,m,A;
int x,y,z;
int f[150010];
int s[150010];
int h[150010];
int v[150010];
int to[300010];
int lg[300010];
int mx[150010];
ll dep[150010];
int vis[150010];
int val[300010];
int head[150010];
int next[300010];
int size[150010];
int g[19][300010];
vector<ll>sum[150010];
vector<pr>root[150010];
vector<ll>fsum[150010];
vector<int>lev[150010];
vector<pr>froot[150010];
vector<int>flev[150010];
inline void add(int x,int y,int z)
{
tot++;
next[tot]=head[x];
head[x]=tot;
to[tot]=y;
val[tot]=z;
}
inline void dfs(int x,int fa)
{
g[0][++dfn]=dep[x];
s[x]=dfn;
for(int i=head[x];i;i=next[i])
{
if(to[i]!=fa)
{
dep[to[i]]=dep[x]+1ll*val[i];
dfs(to[i],x);
g[0][++dfn]=dep[x];
}
}
}
inline void getroot(int x,int fa)
{
size[x]=1;
mx[x]=0;
for(int i=head[x];i;i=next[i])
{
if(!vis[to[i]]&&to[i]!=fa)
{
getroot(to[i],x);
size[x]+=size[to[i]];
mx[x]=max(mx[x],size[to[i]]);
}
}
mx[x]=max(mx[x],num-size[x]);
if(mx[x]<mx[rot])
{
rot=x;
}
}
inline void partation(int x)
{
vis[x]=1;
for(int i=head[x];i;i=next[i])
{
if(!vis[to[i]])
{
num=size[to[i]];
rot=0;
getroot(to[i],0);
f[rot]=x;
partation(rot);
}
}
}
inline int lca(int x,int y)
{
x=s[x];
y=s[y];
if(x>y)
{
swap(x,y);
}
int len=lg[y-x+1];
return min(g[len][x],g[len][y-(1<<len)+1]);
}
inline ll dis(int x,int y)
{
return dep[x]+dep[y]-2ll*lca(x,y);
}
inline void insert(int x,int val)
{
for(int i=x;i;i=f[i])
{
root[i].push_back(make_pair(val,dis(x,i)));
if(f[i])
{
froot[i].push_back(make_pair(val,dis(x,f[i])));
}
}
}
inline ll query(int x,int L,int R)
{
ll res=0;
int l,r;
for(int i=x;i;i=f[i])
{
l=upper_bound(lev[i].begin(),lev[i].end(),L-1)-lev[i].begin()-1;
r=upper_bound(lev[i].begin(),lev[i].end(),R)-lev[i].begin()-1;
res+=sum[i][r]-sum[i][l];
if(f[i])
{
l=upper_bound(flev[i].begin(),flev[i].end(),L-1)-flev[i].begin()-1;
r=upper_bound(flev[i].begin(),flev[i].end(),R)-flev[i].begin()-1;
res-=fsum[i][r]-fsum[i][l];
}
}
for(int i=x;f[i];i=f[i])
{
r=(upper_bound(lev[f[i]].begin(),lev[f[i]].end(),R)-lev[f[i]].begin())-(upper_bound(lev[f[i]].begin(),lev[f[i]].end(),L-1)-lev[f[i]].begin());
l=(upper_bound(flev[i].begin(),flev[i].end(),R)-flev[i].begin())-(upper_bound(flev[i].begin(),flev[i].end(),L-1)-flev[i].begin());
res+=dis(x,f[i])*(r-l);
}
return res;
}
int main()
{
n=read();m=read();A=read();
for(int i=1;i<=n;i++)
{
v[i]=read();
h[i]=v[i];
}
sort(h+1,h+1+n);
for(int i=1;i<=n;i++)
{
v[i]=lower_bound(h+1,h+1+n,v[i])-h;
}
for(int i=1;i<n;i++)
{
x=read();y=read();z=read();
add(x,y,z);
add(y,x,z);
}
dfs(1,0);
mx[0]=1<<30;
num=n;
for(int i=2;i<=dfn;i++)
{
lg[i]=lg[i>>1]+1;
}
for(int j=1;(1<<j)<=dfn;j++)
{
for(int i=1;i+(1<<j)-1<=dfn;i++)
{
g[j][i]=min(g[j-1][i],g[j-1][i+(1<<(j-1))]);
}
}
getroot(1,0);
partation(rot);
for(int i=1;i<=n;i++)
{
insert(i,v[i]);
}
for(int i=1;i<=n;i++)
{
sort(root[i].begin(),root[i].end());
int len=root[i].size();
ll res=0;
lev[i].push_back(0);
sum[i].push_back(0);
for(int j=0;j<len;j++)
{
res+=root[i][j].second;
lev[i].push_back(root[i][j].first);
sum[i].push_back(res);
}
lev[i].push_back(n+1);
sum[i].push_back(1<<30);
sort(froot[i].begin(),froot[i].end());
len=froot[i].size();
res=0;
flev[i].push_back(0);
fsum[i].push_back(0);
for(int j=0;j<len;j++)
{
res+=froot[i][j].second;
flev[i].push_back(froot[i][j].first);
fsum[i].push_back(res);
}
flev[i].push_back(n+1);
fsum[i].push_back(1<<30);
}
while(m--)
{
x=read();a=read();b=read();
l=min((1ll*a+ans)%A,(1ll*b+ans)%A);
r=max((1ll*a+ans)%A,(1ll*b+ans)%A);
l=lower_bound(h+1,h+1+n,l)-h;
r=upper_bound(h+1,h+1+n,r)-h-1;
printf("%lld\n",ans=query(x,l,r));
}
}
BZOJ4012[HNOI2015]开店——树链剖分+可持久化线段树/动态点分治+vector的更多相关文章
- [GDOI2016] 疯狂动物园 [树链剖分+可持久化线段树]
题面 太长了,而且解释的不清楚,我来给个简化版的题意: 给定一棵$n$个点的数,每个点有点权,你需要实现以下$m$个操作 操作1,把$x$到$y$的路径上的所有点的权值都加上$delta$,并且更新一 ...
- 【BZOJ3681】Arietta 树链剖分+可持久化线段树优化建图+网络流
[BZOJ3681]Arietta Description Arietta 的命运与她的妹妹不同,在她的妹妹已经走进学院的时候,她仍然留在山村中.但是她从未停止过和恋人 Velding 的书信往来.一 ...
- 【BZOJ4704】旅行 树链剖分+可持久化线段树
[BZOJ4704]旅行 Description 在Berland,有n个城堡.每个城堡恰好属于一个领主.不同的城堡属于不同的领主.在所有领主中有一个是国王,其他的每个领主都直接隶属于另一位领主,并且 ...
- BZOJ 2243: [SDOI2011]染色 树链剖分 倍增lca 线段树
2243: [SDOI2011]染色 Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://www.lydsy.com/JudgeOnline/pr ...
- BZOJ 2243:染色(树链剖分+区间合并线段树)
[SDOI2011]染色Description给定一棵有n个节点的无根树和m个操作,操作有2类:1.将节点a到节点b路径上所有点都染成颜色c:2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认 ...
- HDU 5029 Relief grain 树链剖分打标记 线段树区间最大值
Relief grain Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://acm.hdu.edu.cn/showproblem.php?pid= ...
- 【Codeforces】【网络流】【树链剖分】【线段树】ALT (CodeForces - 786E)
题意 现在有m个人,每一个人都特别喜欢狗.另外还有一棵n个节点的树. 现在每个人都想要从树上的某个节点走到另外一个节点,且满足要么这个人自带一条狗m,要么他经过的所有边h上都有一条狗. 2<=n ...
- LOJ2269 [SDOI2017] 切树游戏 【FWT】【动态DP】【树链剖分】【线段树】
题目分析: 好题.本来是一道好的非套路题,但是不凑巧的是当年有一位国家集训队员正好介绍了这个算法. 首先考虑静态的情况.这个的DP方程非常容易写出来. 接着可以注意到对于异或结果的计数可以看成一个FW ...
- HYSBZ 4034 【树链剖分】+【线段树 】
<题目链接> 题目大意: 有一棵点数为 N 的树,以点 1 为根,且树点有权值.然后有 M 个 操作,分为三种: 操作 1 :把某个节点 x 的点权增加 a . 操作 2 :把某个节点 x ...
随机推荐
- JAVA,字符串替换
package l515; //例5.15 //字符串替换 public class NewStr { public static void main(String[] args) { String ...
- Go语言之匿名函数
匿名函数和字面量函数一样, 凡是可以使用字面量函数的地方,都可以用匿名函数代替. 这个和js中的匿名函数差不多吧. package main import "fmt" var su ...
- Hadoop数据分析平台项目实战(基于CDH版本集群部署与安装)
1.Hadoop的主要应用场景: a.数据分析平台. b.推荐系统. c.业务系统的底层存储系统. d.业务监控系统. 2.开发环境:Linux集群(Centos64位)+Window开发模式(win ...
- C#学习-索引器
当一个类包含数组成员时,索引器的使用将大大地简化对类中数组成员的访问. 索引器的定义类似于属性,也具有get访问器和set访问器,以下是 [修饰符] 数据类型 this [索引类型index] { g ...
- [转] UniCode编码表
Unicode编码则是采用双字节16位来进行编号,可编65536字符,基本上包含了世界上所有的语言字符,它也就成为了全世界一种通用的编码,而且用十六进制4位表示一个编码,非常简结直观,为大多数开发者所 ...
- Java集合源码学习(二)ArrayList
1.关于ArrayList ArrayList直接继承AbstractList,实现了List. RandomAccess.Cloneable.Serializable接口,为什么叫"Arr ...
- Spring MVC基础知识整理➣环境搭建和Hello World
概述 Spring MVC属于SpringFrameWork的产品,采用Model-View-Controller进行数据交互,已经融合在Spring Web Flow里面.Spring 框架提供了构 ...
- Java基础知识➣多线程编程(五)
概述 Java 给多线程编程提供了内置的支持.一个多线程程序包含两个或多个能并发运行的部分.程序的每一部分都称作一个线程,并且每个线程定义了一个独立的执行路径.使用多线程也是为了充分的利用服务器资源, ...
- 构建ASP.NET MVC5+EF6+EasyUI 1.4.3+Unity4.x注入的后台管理系统(66)-MVC WebApi 用户验证 (2)
前言: 构建ASP.NET MVC5+EF6+EasyUI 1.4.3+Unity4.x注入的后台管理系统(65)-MVC WebApi 用户验证 (1) 回顾上一节,我们利用webapi简单的登录并 ...
- 052 kafka对topic的增删改查操作
一:create 1.开始使用命令 2.创建 bin/kafka-topics.sh --create --topic beifeng --zookeeper linux-hadoop01.ibeif ...