[BZOJ4009][HNOI2015]接水果-整体二分-树状数组-扫描线

接水果

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 小的那个盘子,每个盘子可重复使用(没有使用次数的上限:一个盘子接完一个水果后,后面还可继续接其他水果,只要它是水果路径的子路径)。幽香认为这个游戏很难,你能轻松解决给她看吗?
pic1

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。


车万题日常放福利~
pic
所以树状数组写错了也能调2h+是什么情况……

(╯‵□′)╯︵┻━┻


思路:
考试的时候如果出现这题怕不是只能写暴力……
然而Day1另外两题貌似都不难

考虑到询问很多,可以使用整体二分。
然后就可以把问题转化成:
给定一些树上的路径和一些同样是路径的询问,对于每个询问需要回答有多少路径被这条询问路径覆盖。

根据整体二分的套路,现在咱们需要一个数据结构或者一些预处理~
那么考虑转化问题成可以方便进行维护的东西。
考虑dfs序,设 id[u] u 节点的dfs序上编号,ed[u] u 节点的子树中dfs序的最后一个编号。
对于所有盘子(u,v),有如下两种不同的情况:

1. lca 既不是 u 又不是v
考虑包含这样的盘子的水果的两个端点必然一个在 u 的子树中,另一个在v的子树中。
于是这样的水果满足两个端点分别在 [id[u],ed[u]] id[v],ed[v] 之间。

2. lca u v中的一个。
那么令 lca=u ,同时令 u v所在子树的根节点为 lcas (也就是 v 方向的那个儿子)。
可以发现,覆盖了这个盘子的水果满足其中一个端点在[id[v],ed[v]]中间,而另外一个在 [1,id[lcas]1][ed[lcas]+1,n] 中间。

于是,把所有节点按dfs序标号,把每个水果想象成一个端点。
那么可以发现能覆盖某个盘子的所有水果均会处于一个或两个矩形区域内。
因此把盘子描述成一个或两个矩形,则问题转化为询问一个点被多少矩形覆盖。
那么这就可以扫描线+树状数组了~

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>

using namespace std;

const int N=80009;
const int K=20;

int n,p,q;
int to[N<<1],nxt[N<<1],beg[N],tot;
int id[N],ed[N],seg[N],ans[N],dfn;
int fa[N][K],dep[N],pcnt;

struct query
{
    int u,v,k,id;
}qu[N],qtmp[N];

struct plate
{
    int xl,xr,yl,yr,v;
    bool operator < (plate o)const
    {
        return v<o.v;
    }
}pl[N<<1];

struct op
{
    int ty,px,a,b,c;
    bool operator < (op o)const
    {
        if(px==o.px)
            return ty<o.ty;
        return px<o.px;
    }
}o[N<<2];

namespace bit
{
    int bit[N];
    inline int query(int p)
    {
        int ret=0;
        for(int i=p;i;i-=i&-i)
            ret+=bit[i];
        return ret;
    }
    inline void modify(int l,int r,int v)
    {
        for(int i=l;i<=n;i+=i&-i)
            bit[i]+=v;
        for(int i=r+1;i<=n;i+=i&-i)
            bit[i]-=v;
    }
}

inline int read()
{
    int x=0;char ch=getchar();
    while(ch<'0' || '9'<ch)ch=getchar();
    while('0'<=ch && ch<='9')x=x*10+(ch^48),ch=getchar();
    return x;
}

inline void add(int u,int v)
{
    to[++tot]=v;
    nxt[tot]=beg[u];
    beg[u]=tot;
}

inline void dfs(int u)
{
    seg[id[u]=++dfn]=u;
    for(int i=beg[u],v;i;i=nxt[i])
        if((v=to[i])!=fa[u][0])
        {
            fa[v][0]=u;
            dep[v]=dep[u]+1;
            for(int j=1;j<K;j++)
                fa[v][j]=fa[fa[v][j-1]][j-1];
            dfs(v);
        }
    ed[u]=dfn;
}

inline int up(int u,int d)
{
    for(int i=K-1;i>=0;i--)
        if((d>>i)&1)
            u=fa[u][i];
    return u;
}

inline int lca(int u,int v)
{
    if(dep[u]<dep[v])swap(u,v);
    u=up(u,dep[u]-dep[v]);
    if(u==v)return u;
    for(int i=K-1;i>=0;i--)
        if(fa[u][i]!=fa[v][i])
            u=fa[u][i],v=fa[v][i];
    return fa[u][0];
}

inline void calc(int l,int r,int dl,int dr)
{
    if(l>r)return;
    if(dl==dr)
    {
        for(int i=l;i<=r;i++)
            ans[qu[i].id]=pl[dl].v;
        return;
    }

    int mid=dl+dr>>1,ecnt=0;
    for(int i=dl;i<=mid;i++)
    {
        o[++ecnt]=(op){0,pl[i].xl,pl[i].yl,pl[i].yr,1};
        o[++ecnt]=(op){2,pl[i].xr,pl[i].yl,pl[i].yr,-1};
    }
    for(int i=l;i<=r;i++)
        o[++ecnt]=(op){1,qu[i].u,qu[i].v,i,0};
    sort(o+1,o+ecnt+1);

    int lb=l-1,rb=r+1;
    for(int i=1;i<=ecnt;i++)
        if(o[i].ty!=1)
            bit::modify(o[i].a,o[i].b,o[i].c);
        else
        {
            int ret=bit::query(o[i].a),ind=o[i].b;
            if(qu[ind].k<=ret)
                qtmp[++lb]=qu[ind];
            else
            {
                qu[ind].k-=ret;
                qtmp[--rb]=qu[ind];
            }
        }
    for(int i=l;i<=r;i++)
        qu[i]=qtmp[i];
    calc(l,lb,dl,mid);
    calc(rb,r,mid+1,dr);
}

int main()
{
    n=read();p=read();q=read();
    for(int i=1,u,v;i<n;i++)
    {
        u=read();v=read();
        add(u,v);add(v,u);
    }
    dfs(1);

    for(int i=1,u,v,c;i<=p;i++)
    {
        u=read();v=read();c=read();
        if(id[u]>id[v])swap(u,v);
        int lcas=lca(u,v),son;
        if(lcas!=u)
            pl[++pcnt]=(plate){id[u],ed[u],id[v],ed[v],c};
        else
        {
            pl[++pcnt]=(plate){1,id[son=up(v,dep[v]-dep[u]-1)]-1,id[v],ed[v],c};
            if(ed[son]<n)pl[++pcnt]=(plate){id[v],ed[v],ed[son]+1,n,c};
        }
    }
    sort(pl+1,pl+pcnt+1);

    for(int i=1,u,v,k;i<=q;i++)
    {
        u=read();v=read();k=read();
        if(id[u]>id[v])
            swap(u,v);
        qu[i]=(query){id[u],id[v],k,i};
    }

    calc(1,q,1,pcnt);

    for(int i=1;i<=q;i++)
        printf("%d\n",ans[i]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值