HDU 6162 Ch’s gift

HDU 6162 Ch’s gift

线段树,离线

题意

给一棵树,每次询问:uv两点间的路上(包括uv),点权在ab之间的所有点点权和。

思路

由于没有修改操作,一个显然的想法是离线处理所有问题
将询问拆成1-x,1-y,1-LCA(x,y),则处理的问题转化为从根到节点的链上的问题。
解决这个问题,我们可以在dfs时向treap插入当前的数,在退出时删除这个数,并且每次维护在该点上的答案。

两种思路,在线的树链剖分加主席树维护,离线的LCA加线段树/treap。我写的是离线LCA加线段树。线段树当成权值线段树用,所以离散化特别麻烦,可能treap更简单。

将每个询问拆开,u到v拆成:(1~u)+(1~v)-(2*1~LCA(u,v))+LCA(u,v)。如果LCA(u,v)权在询问上下界之间,就作为附加值加上,否则不加。

因为权值线段树要求范围是1到n,所以需要离散化,注意各种边界问题。

代码

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<set>
#include<iostream>
#include<vector>
#include<queue>
#include<regex>
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define M(a,b) memset(a,b,sizeof(a))
using namespace std;
const int MAXN=100005;
const int oo=0x3f3f3f3f;
typedef long long LL;

//树结构
struct Edge
{
    int to, ne;
}e[MAXN<<1];
int head[MAXN], v[MAXN], edgenum;
void addedge(int u, int v)
{
    e[edgenum].to=v, e[edgenum].ne=head[u], head[u]=edgenum++;
    e[edgenum].to=u, e[edgenum].ne=head[v], head[v]=edgenum++;
}

//LCA
int parent[20][MAXN];
int depth[MAXN];
void ddfs(int v, int p, int d)
{
    parent[0][v]=p;
    depth[v]=d;
    for(int i=head[v];~i;i=e[i].ne)
    {
        if(e[i].to!=p) ddfs(e[i].to, v, d+1);
    }
}
void init(int V)
{
    ddfs(1, -1, 0);
    for(int k=0;k+1<20;k++)
    {
        for(int v=1;v<=V;v++)
        {
            if(parent[k][v]<0) parent[k+1][v]=-1;
            else parent[k+1][v]=parent[k][parent[k][v]];
        }
    }
}
int lca(int u, int v)
{
    if(depth[u]>depth[v]) swap(u, v);
    for(int k=0;k<20;k++)
    {
        if((depth[v]-depth[u])>>k&1)
            v=parent[k][v];
    }
    if(u==v) return u;
    for(int k=20-1;k>=0;k--)
    {
        if(parent[k][u]!=parent[k][v])
        {
            u=parent[k][u];
            v=parent[k][v];
        }
    }
    return parent[0][u];
}


//点权与哈希
struct Val
{
    LL val;
    int i;
    int ha;
}val[MAXN];
bool cmp1(Val a, Val b)
{
    return a.val<b.val;
}
bool cmp2(Val a, Val b)
{
    return a.i<b.i;
}
LL ha[MAXN];

//分解询问
struct Query
{
    int x, y;
    int i, k;
    LL res;
    int ne;
    int up, dw;
    LL add;
}q[MAXN*3];
int qnum, headq[MAXN];//链式前向星储存询问

//线段树
LL stree[MAXN<<2];
void pushup(int rt) { stree[rt]=stree[rt<<1]+stree[rt<<1|1]; }
void build() { M(stree, 0); }
void update(int pos, LL c, int l, int r, int rt)
{
    if(l==r) { stree[rt]+=c;return; }
    int mid=(l+r)>>1;
    if(pos<=mid) update(pos, c, lson);
    else update(pos, c, rson);
    pushup(rt);
}
LL query(int L, int R, int l, int r, int rt)
{
    if(L<=l&&r<=R) return stree[rt];
    int mid=(l+r)>>1;
    LL res=0;
    if(L<=mid) res+=query(L, R, lson);
    if(mid<R) res+=query(L, R, rson);
    return res;
}

//dfs的solve
void dfs(int u, int fa, int n)
{
    update(val[u].ha, val[u].val, 1, n, 1);
    for(int i=headq[u];~i;i=q[i].ne)
    {
        int l=q[i].dw, r=q[i].up;
        q[i].res=query(l, r, 1, n, 1);
    }

    for(int i=head[u];~i;i=e[i].ne)
    {
        int to=e[i].to;
        if(to!=fa) dfs(to, u, n);
    }

    update(val[u].ha, -val[u].val, 1, n, 1);
}

int main()
{
    //freopen("in.txt", "r", stdin);
    //freopen("ou.txt", "w", stdout);
    int n, m;
    while(scanf("%d%d", &n, &m)==2)
    {
        //初始化
        edgenum=1, qnum=1;M(head, -1), M(headq, -1);
        build();

        //点权与哈希
        for(int i=1;i<=n;i++)
        {
            scanf("%lld", &val[i].val), val[i].i=i;
        }
        sort(val+1, val+n+1, cmp1);
        int tt=1;ha[tt]=val[1].val-1;tt++;//加前哨兵
        val[1].ha=tt;ha[tt]=val[1].val;
        for(int i=2;i<=n;i++)
        {
            if(val[i].val>val[i-1].val) val[i].ha=++tt, ha[tt]=val[i].val;
            else val[i].ha=tt;
        }
        sort(val+1, val+n+1, cmp2);

        //树结构
        for(int i=1;i<n;i++)
        {
            int u, v;scanf("%d%d", &u, &v);
            addedge(u, v);
        }

        init(n);//LCA初始化

        //分解询问
        for(int i=1;i<=m;i++)
        {
            int x, y, up, dw;scanf("%d%d%d%d", &x, &y, &dw, &up);
            int u=lca(x, y);
            q[qnum].x=1, q[qnum].y=x, q[qnum].i=i, q[qnum].k=1;
            q[qnum].dw=lower_bound(ha+1, ha+1+tt, dw)-ha, q[qnum].up=lower_bound(ha+1, ha+1+tt, up)-ha;
            if(up<ha[q[qnum].up]) q[qnum].up--;//注意特判上端点
            q[qnum].ne=headq[x], headq[x]=qnum++;

            q[qnum].x=1, q[qnum].y=y, q[qnum].i=i, q[qnum].k=1;
            q[qnum].dw=lower_bound(ha+1, ha+1+tt, dw)-ha, q[qnum].up=lower_bound(ha+1, ha+1+tt, up)-ha;
            if(up<ha[q[qnum].up]) q[qnum].up--;//注意特判上端点
            q[qnum].ne=headq[y], headq[y]=qnum++;

            q[qnum].x=1, q[qnum].y=u, q[qnum].i=i, q[qnum].k=-1;
            q[qnum].dw=lower_bound(ha+1, ha+1+tt, dw)-ha, q[qnum].up=lower_bound(ha+1, ha+1+tt, up)-ha;
            if(up<ha[q[qnum].up]) q[qnum].up--;//注意特判上端点
            q[qnum].ne=headq[u], headq[u]=qnum++;

            q[qnum-1].add=((val[u].val>=dw&&val[u].val<=up) ? val[u].val : 0);//是否附加LCA的权
        }


        dfs(1, -1, tt);
        for(int i=1;i<=3*m;i+=3)
        {
            LL ans=q[i].res+q[i+1].res-2*q[i+2].res+q[i+2].add;
            printf("%lld%c", ans, i==3*m-2 ? '\n' : ' ');
        }

    }
    //system("pause");
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值