[BZOJ 4012][HNOI2015]开店(树链剖分+主席树)

Description

风见幽香有一个好朋友叫八云紫,她们经常一起看星星看月亮从诗词歌赋谈到人生哲学。最近她们灵机一动,打算在幻想乡开一家小店来做生意赚点钱。这样的想法当然非常好啦,但是她们也发现她们面临着一个问题,那就是店开在哪里,面向什么样的人群。很神奇的是,幻想乡的地图是一个树形结构,幻想乡一共有 n个地方,编号为 1 到 n,被 n-1 条带权的边连接起来。每个地方都住着一个妖怪,其中第 i 个地方的妖怪年龄是 x_i。妖怪都是些比较喜欢安静的家伙,所以它们并不希望和很多妖怪相邻。所以这个树所有顶点的度数都小于或等于 3。妖怪和人一样,兴趣点随着年龄的变化自然就会变化,比如我们的 18 岁少女幽香和八云紫就比较喜欢可爱的东西。幽香通过研究发现,基本上妖怪的兴趣只跟年龄有关,所以幽香打算选择一个地方 u(u为编号),然后在 u开一家面向年龄在 L到R 之间(即年龄大于等于 L、小于等于 R)的妖怪的店。也有可能 u这个地方离这些妖怪比较远,于是幽香就想要知道所有年龄在 L 到 R 之间的妖怪,到点 u 的距离的和是多少(妖怪到 u 的距离是该妖怪所在地方到 u 的路径上的边的权之和) ,幽香把这个称为这个开店方案的方便值。幽香她们还没有决定要把店开在哪里,八云紫倒是准备了很多方案,于是幽香想要知道,对于每个方案,方便值是多少呢。

Solution

和LCA那道是有一些相似的,没有A那道题的可以先去看一下√

本来是一道动态树分治?听说可以用树剖就很开心地去码了,结果代码能力太弱各种WAQAQ

最后还是抄了神犇代码

将年龄离散化以后按顺序加入,每次把这个点到root的边加进主席树

点u、v路径上的边权之和即 u到root的边权+v到root的边权-lca(u,v)到root的边权*2

则年龄在L到R间的妖怪到u的边权之和即 ∑{dis[v]+dis[lca(u,v)]*2}+dis[u]*妖怪个数(L<=v<=R)

dis[lca(u,v)]通过在主席树(L~R间的)上查询u到根的边权和得出,其余的可以用前缀和维护

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define N 150005
#define Q 200005
using namespace std;
typedef long long LL;
int n,q,A,x[N],y[N],head[N],cnt=0;
int father[N],deep[N],pos[N],top[N],siz[N],maxv[N],sz=0;
int rt[N],ls[N*120],rs[N*120],tot=0;
LL sum[N*120],lazy[N*120],dis[N],d[N],w[N],p1[N],p2[N];
LL read()
{
    LL x=0,f=1;char c=getchar();
    while(c<‘0‘||c>‘9‘){if(c==‘-‘)f=-1;c=getchar();}
    while(c>=‘0‘&&c<=‘9‘){x=x*10+c-‘0‘;c=getchar();}
    return x*f;
}
struct Node1
{
    int next,to,w;
}Edges[N*2];
struct Node2
{
    int age,id;
    Node2(int age=0,int id=0):age(age),id(id){}
    bool operator < (const Node2& t) const
    {return age<t.age;}
}data[N];
void addedge(int u,int v,int w)
{
    Edges[++cnt].next=head[u];
    head[u]=cnt;
    Edges[cnt].to=v;
    Edges[cnt].w=w;
}
void dfs1(int u)
{
    siz[u]=1;
    for(int i=head[u];~i;i=Edges[i].next)
    {
        int v=Edges[i].to;
        if(v==father[u])continue;
        father[v]=u;
        deep[v]=deep[u]+1,d[v]=Edges[i].w,dis[v]=dis[u]+Edges[i].w;
        dfs1(v);siz[u]+=siz[v];
        if(!maxv[u]||siz[v]>siz[maxv[u]])maxv[u]=v;
    }
}
void dfs2(int u,int t)
{
    top[u]=t;
    ++sz,pos[u]=sz;
    w[sz]=w[sz-1]+d[u],p1[x[u]]++,p2[x[u]]+=dis[u];
    if(maxv[u])dfs2(maxv[u],t);
    for(int i=head[u];~i;i=Edges[i].next)
    {
        int v=Edges[i].to;
        if(v==maxv[u]||v==father[u])continue;
        dfs2(v,v);
    }
}
void insert(int &idx,int last,int l,int r,int L,int R)
{
    ++tot,idx=tot;
    sum[idx]=sum[last],lazy[idx]=lazy[last],ls[idx]=ls[last],rs[idx]=rs[last];
    if(L==l&&R==r){lazy[idx]++;sum[idx]+=w[r]-w[l-1];return;}
    int mid=(l+r)>>1;
    if(R<=mid)insert(ls[idx],ls[last],l,mid,L,R);
    else if(L>mid)insert(rs[idx],rs[last],mid+1,r,L,R);
    else insert(ls[idx],ls[last],l,mid,L,mid),insert(rs[idx],rs[last],mid+1,r,mid+1,R);
    sum[idx]=sum[ls[idx]]+sum[rs[idx]]+lazy[idx]*(w[r]-w[l-1]);
}
LL query(int s,int t,int l,int r,int L,int R,int tag)
{
    if(L==l&&R==r)return sum[s]-sum[t]+1LL*tag*(w[r]-w[l-1]);
    int mid=(l+r)>>1;tag+=lazy[s]-lazy[t];
    if(R<=mid)return query(ls[s],ls[t],l,mid,L,R,tag);
    else if(L>mid)return query(rs[s],rs[t],mid+1,r,L,R,tag);
    else return query(ls[s],ls[t],l,mid,L,mid,tag)+query(rs[s],rs[t],mid+1,r,mid+1,R,tag);
}
void add(int i,int x)
{
    while(x)
    {
        insert(rt[data[i].age],rt[data[i].age],1,sz,pos[top[x]],pos[x]);
        x=father[top[x]];
    }
}
LL ask(int l,int r,int u)
{
    LL res=0;
    while(u)
    {
        res+=query(rt[r],rt[l-1],1,sz,pos[top[u]],pos[u],0);
        u=father[top[u]];
    }
    return res;
}
int main()
{
    memset(head,-1,sizeof(head));
    n=read(),q=read(),A=read();
    for(int i=1;i<=n;i++)y[i]=x[i]=read();
    sort(y+1,y+1+n);
    int t=unique(y+1,y+1+n)-y-1;
    for(int i=1;i<=n;i++)x[i]=lower_bound(y+1,y+1+t,x[i])-y;
    for(int i=1;i<n;i++)
    {
        int a=read(),b=read(),c=read();
        addedge(a,b,c),addedge(b,a,c);
    }
    dfs1(1);dfs2(1,1);
    for(int i=1;i<=n;i++)data[i]=Node2(x[i],i);
    sort(data+1,data+1+n);
    for(int i=1;i<=n;i++)
    {
        if(!rt[data[i].age])
        rt[data[i].age]=rt[data[i].age-1];
        add(i,data[i].id);
    }
    for(int i=1;i<=t;i++)p1[i]+=p1[i-1],p2[i]+=p2[i-1];
    LL last=0;
    for(int i=1;i<=q;i++)
    {
        LL u=read(),a=read(),b=read();
        int l=min((a+last)%A,(b+last)%A),r=max((a+last)%A,(b+last)%A);
        l=lower_bound(y+1,y+1+t,l)-y,r=upper_bound(y+1,y+1+t,r)-y-1;
        if(l>r||l>t||r<1){last=0;printf("0\n");continue;}
        last=dis[u]*(p1[r]-p1[l-1])+p2[r]-p2[l-1]-2*ask(l,r,u);
        printf("%lld\n",last);
    }
    return 0;
}

---恢复内容结束---

时间: 2024-12-26 21:19:07

[BZOJ 4012][HNOI2015]开店(树链剖分+主席树)的相关文章

hdu 6162 Ch’s gift(树链剖分+主席树)

题目链接:hdu 6162 Ch's gift 题意: 给你一棵树,树上每个点有一个权值,现在有m个询问,每次询问给你一个s,t,L,R,问你从s到t的路径上,权值在[L,R]内的总和为多少. 题解: 我感觉我写复杂了,用树链剖分来维护路径,然后用主席树来建立权值线段树乱搞. 1 #include<bits/stdc++.h> 2 #define mst(a,b) memset(a,b,sizeof(a)) 3 #define F(i,a,b) for(int i=(a);i<=(b);

BZOJ1146 [CTSC2008]网络管理Network 树链剖分 主席树 树状数组

欢迎访问~原文出处--博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ1146 题意概括 在一棵树上,每一个点一个权值. 有两种操作: 1.单点修改 2.询问两点之间的树链上的第k大值 题解 水题. 就是烦了一点. 树链剖分+带修主席树. 带修主席树: BZOJ1901 Zju2112 Dynamic Rankings 主席树 代码 #include <cstring> #include <cstdio> #include <algorithm&g

BZOJ3531 SDOI2014 旅行 - 树链剖分,主席树

题意:给定一棵树,树上每个点有权值和类型.支持:修改某个点的类型:修改某个点的权值:询问某条链上某个类型的点的和/最大值.点数/类型数/询问数<=100000. 分析: 树链剖分,对每个类型的点建立线段树(动态开点). note: 忘记写t_query返回值调半天-- 莫名其妙地1A 代码: 1 #include <bits/stdc++.h> 2 using namespace std; 3 4 int a[5000005],s[5000005],dep[100005],size[10

bzoj2588 -- 树链剖分+主席树

先将权值离散. 显然可以对于每个结点建一棵权值线段树存这个点到根结点的路径上的点权,询问时在线段树上二分,但这样时间是O(n2log2n)的. 然后想到用主席树优化,时间复杂度O(n*log2n). 代码: 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<vector> 6 using namespace st

2019年ICPC南昌网络赛 J. Distance on the tree 树链剖分+主席树

边权转点权,每次遍历到下一个点,把走个这条边的权值加入主席树中即可. #include<iostream> #include<algorithm> #include<stdio.h> #include<string.h> using namespace std; const int maxx = 2e5+10; struct node{ int l,r,cnt; }tree[maxx*40]; int head[maxx],rk[maxx],siz[maxx

Codechef FIBTREE 树链剖分 主席树 LCA 二次剩余 快速幂

原文链接https://www.cnblogs.com/zhouzhendong/p/CC-FIBTREE.html 题目传送门 - CC-FIBTREE 题意 题解 代码 #include <bits/stdc++.h> using namespace std; typedef long long LL; const int N=100005,S=N*200,mod=1e9+9; struct Gragh{ static const int M=N*2; int cnt,y[M],nxt[M

【BZOJ】1146: [CTSC2008]网络管理Network(树链剖分+线段树套平衡树+二分 / dfs序+树状数组+主席树)

第一种做法(时间太感人): 这题我真的逗了,调了一下午,疯狂造数据,始终找不到错. 后来发现自己sb了,更新那里没有打id,直接套上u了.我.... 调了一下午啊!一下午的时光啊!本来说好中午A掉去学习第二种做法,噗 好吧,现在第一种做法是hld+seg+bst+二分,常数巨大,log^4级别,目前只会这种. 树剖后仍然用线段树维护dfs序区间,然后在每个区间建一颗平衡树,我用treap,(这题找最大啊,,,囧,并且要注意,这里的rank是比他大的数量,so,我们在二分时判断要判断一个范围,即要

bzoj 2157: 旅游【树链剖分+线段树】

裸的树链剖分+线段树 但是要注意一个地方--我WA了好几次才发现取完相反数之后max值和min值是要交换的-- #include<iostream> #include<cstdio> using namespace std; const int N=200005; int n,m,h[N],cnt,de[N],va[N],fa[N],si[N],hs[N],fr[N],id[N],tot,rl[N]; char c[10]; struct qwe { int ne,no,to,va

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.php?pid=39566 Description Given a tree with n (1 ≤ n ≤ 200,000) nodes and a list of q (1 ≤ q ≤ 100,000) queries, process the queries in order and out