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)); } }