bzoj 4009: [HNOI2015]接水果 整体二分+k-d tree

Description

风见幽香非常喜欢玩一个叫做 osu!的游戏,其中她最喜欢玩的模式就是接水果。
由于她已经DT FC 了The big black, 她觉得这个游戏太简单了,于是发明了一个更
加难的版本。首先有一个地图,是一棵由 n 个顶点、n-1 条边组成的树(例如图 1
给出的树包含 8 个顶点、7 条边)。这颗树上有 P 个盘子,每个盘子实际上是一条
路径(例如图 1 中顶点 6 到顶点 8 的路径),并且每个盘子还有一个权值。第 i 个
盘子就是顶点a_i到顶点b_i的路径(由于是树,所以从a_i到b_i的路径是唯一的),
权值为c_i。接下来依次会有Q个水果掉下来,每个水果本质上也是一条路径,第
i 个水果是从顶点 u_i 到顶点v_i 的路径。幽香每次需要选择一个盘子去接当前的水
果:一个盘子能接住一个水果,当且仅当盘子的路径是水果的路径的子路径(例如
图1中从 3到7 的路径是从1到8的路径的子路径)。这里规定:从a 到b的路径与
从b到 a的路径是同一条路径。当然为了提高难度,对于第 i 个水果,你需要选择
能接住它的所有盘子中,权值第 k_i 小的那个盘子,每个盘子可重复使用(没有使用次数
的上限:一个盘子接完一个水果后,后面还可继续接其他水果,只要它是水
果路径的子路径)。幽香认为这个游戏很难,你能轻松解决给她看吗?

Input
第一行三个数 n和P 和Q,表示树的大小和盘子的个数和水果的个数。
接下来n-1 行,每行两个数 a、b,表示树上的a和b 之间有一条边。树中顶点
按1到 n标号。 接下来 P 行,每行三个数 a、b、c,表示路径为 a 到 b、权值为 c 的盘子,其
中0≤c≤10^9,a不等于b。
接下来Q行,每行三个数 u、v、k,表示路径为 u到 v的水果,其中 u不等于v,你需要选择第 k小的盘子,
第k 小一定存在。

Output
对于每个果子,输出一行表示选择的盘子的权值。

Sample Input
10 10 10
1 2
2 3
3 4
4 5
5 6
6 7
7 8
8 9
9 10
3 2 217394434
10 7 13022269
6 7 283254485
6 8 333042360
4 6 442139372
8 3 225045590
10 4 922205209
10 8 808296330
9 2 486331361
4 9 551176338
1 8 5
3 8 3
3 8 4
1 8 3
4 8 1
2 3 1
2 3 1
2 3 1
2 4 1
1 4 1

Sample Output
442139372
333042360
442139372
283254485
283254485
217394434
217394434
217394434
217394434
217394434

HINT
N,P,Q<=40000。

分析:
k k k小我们可以二分一个答案,再计算有多少个是他的子路径。
所以考虑整体二分,对于一个盘子(子路径),从 x x x y y y。假设 d e p [ x ] &lt; d e p [ y ] dep[x]&lt;dep[y] dep[x]<dep[y],如果 x x x y y y的父亲,那么只有一个端点在 y y y的子树内,另一个端点是 x x x的在路径上的儿子补集才满足;如果 x x x不是 y y y的父亲,那么一个端点在 x x x子树内,另一个端点在 y y y的子树内的才满足。
所以我们把从 x x x y y y果子(路径)看做一个点 ( d f n [ x ] , d f n [ y ] ) (dfn[x],dfn[y]) (dfn[x],dfn[y]),对于子路径对一个矩阵+1,因为可以交换起点和终点,所以矩阵的横纵坐标交换再加一次即可。

复杂度是 O ( n l o g 3 n ) O(nlog^3n) O(nlog3n)

代码:

// luogu-judger-enable-o2
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>

const int N=40007;

using namespace std;

int n,m,T,x,y,cnt,root;
int ls[N],dfn[N][2],dep[N],f[N][16];

struct edge{
    int y,next;
}g[N*2];

struct rec{
    int d[2];
};

bool operator ==(rec a,rec b)
{
    return (a.d[0]==b.d[0]) && (a.d[1]==b.d[1]); 
}

struct node{
    int l,r,data;
}t[N*300];

struct line{
    int x,y,w;
}a[N];

bool cmp1(line a,line b)
{
    return a.w<b.w;
}

struct query{
    int x,y,k,ans,num;
}q[N],L[N],R[N];

bool cmp2(query a,query b)
{
    return a.num<b.num;
}

void add(int x,int y)
{
    g[++cnt]=(edge){y,ls[x]};
    ls[x]=cnt;
}

void dfs(int x,int fa)
{
    dfn[x][0]=dfn[x][1]=++cnt;
    f[x][0]=fa;
    dep[x]=dep[fa]+1;
    for (int i=ls[x];i>0;i=g[i].next)
    {
        int y=g[i].y;
        if (y==fa) continue;
        dfs(y,x);
        dfn[x][1]=dfn[y][1];
    }
}

int find(int x,int y)
{
    int k=15,t=1<<k;
    int d=dep[y]-dep[x]-1;
    while (d)
    {
        if (d>=t)
        {
            y=f[y][k];
            d-=t;
        }
        k--,t>>=1;
    }
    return y;
}

bool isfather(int x,int y)
{
    return (dfn[x][0]<=dfn[y][0]) && (dfn[x][1]>=dfn[y][1]);
}

void build(int &p,rec l,rec r,rec a,int op)
{
    if (!p)
    {
        p=++cnt;
        t[p].l=t[p].r=t[p].data=0;
    }
    if (l==r) return;
    int mid=(l.d[op]+r.d[op])/2;
    rec L=l,R=r;
    L.d[op]=mid+1,R.d[op]=mid;
    if (a.d[op]<=mid) build(t[p].l,l,R,a,op^1);
                 else build(t[p].r,L,r,a,op^1);
}

void ins(int &p,rec l,rec r,rec a,rec b,int k,int op)
{
    if (!p) return;
    if ((l==a) && (r==b))
    {
        t[p].data+=k;
        return;
    }
    int mid=(l.d[op]+r.d[op])/2;
    rec L=l,R=r;
    L.d[op]=mid+1,R.d[op]=mid;
    if (b.d[op]<=mid) ins(t[p].l,l,R,a,b,k,op^1);
    else if (a.d[op]>mid) ins(t[p].r,L,r,a,b,k,op^1);
    else
    {
    	rec A=a,B=b;
    	A.d[op]=mid+1,B.d[op]=mid;
    	ins(t[p].l,l,R,a,B,k,op^1);
    	ins(t[p].r,L,r,A,b,k,op^1);
    }
}

int query(int &p,rec l,rec r,rec a,int op)
{
    if (l==r) return t[p].data;
    int mid=(l.d[op]+r.d[op])/2;
    rec L=l,R=r;
    L.d[op]=mid+1,R.d[op]=mid;
    if (a.d[op]<=mid) return query(t[p].l,l,R,a,op^1)+t[p].data;
                 else return query(t[p].r,L,r,a,op^1)+t[p].data;
}

void solve(int l,int r,int ql,int qr)
{
    if (ql>qr) return;
    if (l==r)
    {
        for (int i=ql;i<=qr;i++) q[i].ans=a[l].w;
        return;
    }
    int mid=(l+r)/2;
    cnt=root=0;
    
    for (int i=ql;i<=qr;i++)
    {
        int x=q[i].x,y=q[i].y;
        build(root,(rec){{1,1}},(rec){{n,n}},(rec){{dfn[x][0],dfn[y][0]}},0);
    }
    for (int i=l;i<=mid;i++)
    {
        int x=a[i].x,y=a[i].y;
        if (x>y) swap(x,y);
        if (isfather(x,y))
        {
            int d=find(x,y);
            if (dfn[d][0]>1) ins(root,(rec){{1,1}},(rec){{n,n}},(rec){{1,dfn[y][0]}},(rec){{dfn[d][0]-1,dfn[y][1]}},1,0);
            if (dfn[d][0]>1) ins(root,(rec){{1,1}},(rec){{n,n}},(rec){{dfn[y][0],1}},(rec){{dfn[y][1],dfn[d][0]-1}},1,0);
            if (dfn[d][1]<n) ins(root,(rec){{1,1}},(rec){{n,n}},(rec){{dfn[d][1]+1,dfn[y][0]}},(rec){{n,dfn[y][1]}},1,0);
            if (dfn[d][1]<n) ins(root,(rec){{1,1}},(rec){{n,n}},(rec){{dfn[y][0],dfn[d][1]+1}},(rec){{dfn[y][1],n}},1,0);
        }
        else
        {
            ins(root,(rec){{1,1}},(rec){{n,n}},(rec){{dfn[x][0],dfn[y][0]}},(rec){{dfn[x][1],dfn[y][1]}},1,0);
            ins(root,(rec){{1,1}},(rec){{n,n}},(rec){{dfn[y][0],dfn[x][0]}},(rec){{dfn[y][1],dfn[x][1]}},1,0);
        }
    }	
    int cnt1=0,cnt2=0;
    for (int i=ql;i<=qr;i++)
    {
        int x=q[i].x,y=q[i].y;
        int d=query(root,(rec){{1,1}},(rec){{n,n}},(rec){{dfn[x][0],dfn[y][0]}},0);
        if (q[i].k<=d) L[++cnt1]=q[i];
                  else q[i].k-=d,R[++cnt2]=q[i];
    }
    for (int i=1;i<=cnt1;i++) q[ql+i-1]=L[i];
    for (int i=1;i<=cnt2;i++) q[ql+cnt1+i-1]=R[i];
    solve(l,mid,ql,ql+cnt1-1);
    solve(mid+1,r,ql+cnt1,qr);
}

int main()
{
    scanf("%d%d%d",&n,&m,&T);
    for (int i=1;i<n;i++)
    {
        scanf("%d%d",&x,&y);
        add(x,y);
        add(y,x);
    }
    cnt=0;
    dfs(1,0);
    for (int j=1;j<16;j++)
    {
        for (int i=1;i<=n;i++) f[i][j]=f[f[i][j-1]][j-1];
    }
    for (int i=1;i<=m;i++) scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].w);
    sort(a+1,a+m+1,cmp1);
    for (int i=1;i<=T;i++)
    {
        scanf("%d%d%d",&q[i].x,&q[i].y,&q[i].k);
        q[i].num=i;
    }
    solve(1,m,1,T);
    sort(q+1,q+T+1,cmp2);
    for (int i=1;i<=T;i++) printf("%d\n",q[i].ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值