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 ...
随机推荐
- 加密算法之非对称加密RSA
一:非对称加密的由来 RSA公钥加密算法是1977年由Ron Rivest.Adi Shamirh和LenAdleman在(美国麻省理工学院)开发的.RSA取名来自开发他们三者的名字.RSA是目前最有 ...
- CSS预处理器—Sass、LESS和Stylus
http://www.w3cplus.com/css/css-preprocessor-sass-vs-less-stylus-2.html 一.什么是CSS预处器 CSS预处理器定义了一种新的语言, ...
- linux服务器性能——CPU、内存、流量、磁盘使用率的监控
https://blog.csdn.net/u012859748/article/details/72731080
- java web获取请求体内容
Java Web中如何获取请求体内容呢? 我们知道请求方式分为两种:Get,Post. /*** * Compatible with GET and POST * * @param request * ...
- 5336: [TJOI2018]party
题解: 比较水啦..dp套dp f[i][j][k]表示枚举了前i位,最大公共子序列匹配状态为j,noi匹配到了第k位 因为g[j]和g[j+1]最多差1 所以可以状压成j 然后内层再dp一下搞出下一 ...
- python全栈开发day66-视图系统、路由系统
一.昨日内容回顾 1. tags 1. for循环 {% for name in name_list %} {{ name }} {% endfor %} {% for name in name_li ...
- Kudu Native RDD
Spark与Kudu的集成同事提供了kudu RDD import org.apache.kudu.spark.kudu.KuduContext import org.apache.spark.{Sp ...
- gitlab之三: gitlab邮件通知的配置
参考 : https://www.cnblogs.com/lovelinux199075/p/9072265.html gitlab 添加新用户后,会自动发送邮件到填写的邮箱. 实验版本: 11. ...
- BZOJ4319 cerc2008 Suffix reconstruction 字符串 SA
原文链接http://www.cnblogs.com/zhouzhendong/p/9016336.html 题目传送门 - BZOJ4319 题意 给出一个$1,2,\cdots,n$的排列,第$i ...
- HDU3718 Similarity KM
原文链接http://www.cnblogs.com/zhouzhendong/p/8284763.html 题目传送门 - HDU3718 题意概括 直接描述输入吧 首先一个T(T<15),表 ...