BZOJ 4009 接水果

Description

风见幽香非常喜欢玩一个叫做osu!的游戏,其中她最喜欢玩的模式就是接水果。
由于她已经DT FC了The big black, 她觉得这个游戏太简单了,于是发明了一个更加难的版本。首先有一个地图,是一棵由\(n\)个顶点、\(n-1\)条边组成的树。这颗树上有\(P\)个盘子,每个盘子实际上是一条路径,并且每个盘子还有一个权值。第\(i\)个盘子就是顶点\(a_{i}\)到顶点\(b_{i}\)的路径(由于是树,所以从\(a_{i}\)\(b_{i}\)的路径是唯一的),权值为\(c_{i}\)。接下来依次会有\(Q\)个水果掉下来,每个水果本质上也是一条路径,第\(i\)个水果是从顶点\(u_{i}\)到顶点\(v_{i}\)的路径。幽香每次需要选择一个盘子去接当前的水果:一个盘子能接住一个水果,当且仅当盘子的路径是水果的路径的子路径。这里规定:从\(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 \le c \le 10^{9},a \neq b\)
接下来\(Q\)行,每行三个数\(u,v,k\),表示路径为\(u\)\(v\)的水果,其中\(u \neq 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 \le 40000\)

整体二分。对于区间\(l \sim r\),二分值域\(mid\),将\(\le mid\)的盘子加入,每个水果的\(k\)与其路径上存在的盘子数进行比较,若多了该询问往左区间\(l \sim mid\)递归,否则往右区间\(mid+1 \sim r\)递归。
现在的问题就是如何判定每个水果的\(k\)与其路径上存在的盘子数的大小关系。考虑每个盘子,覆盖的路径为\(a_{i}\)\(b_{i}\)\(dep_{a_{i}} < dep_{b_{i}}\)),那么这个盘子所贡献的水果只有可能两种情况:
\(1.b_{i}\)\(a_{i}\)的子树内,那么水果的两个端点\(u,v\)必须一个\(b_{i}\)的子树内,一个在\(a_{i}\)的子树外(可以包括\(a_{i}\))。
\(2.\)否则,两个点必须一个在\(a_{i}\)的子树内,一个在\(b_{i}\)的子树内。
看到有子树的东西,马上想到dfs序。由于这个dfs序是一段连续的区间,所以我们可以将盘子根据dfs序抽象成为矩形(情况一上两个不相交的矩形,情况二是一个矩形),将水果抽象成为一个点,每次检验即询问点被多少矩形所覆盖。扫描线+树状数组即可解决。
注意:矩形必须保证\(x\)坐标小于\(y\)坐标。

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
using namespace std;

#define maxn (40010)
int N,P,Q,side[maxn],next[maxn*2],toit[maxn*2],dep[maxn],tree[maxn];
int L[maxn],R[maxn],ans[maxn],f[maxn][20],Ts,cnt,tot,have[maxn],sum[maxn];
struct SCAN
{
    int X,Y1,Y2,sign,be;
    friend inline bool operator <(const SCAN &a,const SCAN &b) { return a.X != b.X?a.X < b.X:a.sign<b.sign; }
}bac[8*maxn],save[maxn][4];

inline int jump(int a,int step)
{
    for (int i = 0;step;step >>= 1,++i) if (step&1) a = f[a][i];
    return a;
}

inline int lowbit(int a) { return a&-a; }
inline void modify(int a,int b) { for (;a <= N;a += lowbit(a)) tree[a] += b; }
inline int calc(int a) { int ret = 0; for (;a;a -= lowbit(a)) ret += tree[a]; return ret; }

struct NODE
{
    int a,b,c,id;
    friend inline bool operator <(const NODE &x,const NODE &y) { return x.c < y.c; }
    inline void read(int i) { scanf("%d %d %d",&a,&b,&c); id = i; }
    inline void update() { if (L[a] > L[b]) swap(a,b); }
    inline void ins1() { bac[++tot] = (SCAN){L[a],L[b],L[b],0,id}; }
    inline void ins2() { for (int i = 0;i < have[id];++i) bac[++tot] = save[id][i]; }
    inline void change()
    {
        if (L[b] >= L[a]&&L[b] <= R[a])
        {
            int son = jump(b,dep[b]-dep[a]-1);
            if (L[son] > 1)
            {
                save[id][have[id]++] = (SCAN){1,L[b],R[b],-1,id};
                save[id][have[id]++] = (SCAN){L[son]-1,L[b],R[b],1,id};
            }
            if (R[son] < N)
            {
                save[id][have[id]++] = (SCAN){L[b],R[son]+1,N,-1,id};
                save[id][have[id]++] = (SCAN){R[b],R[son]+1,N,1,id};
            }
        }
        else
        {
            save[id][have[id]++] = (SCAN){L[a],L[b],R[b],-1,id};
            save[id][have[id]++] = (SCAN){R[a],L[b],R[b],1,id};
        }
    }
}path[maxn],query[maxn],tmp[maxn];

inline void add(int a,int b) { next[++cnt] = side[a]; side[a] = cnt; toit[cnt] = b; }
inline void ins(int a,int b) { add(a,b); add(b,a); }

inline void dfs(int now)
{
    L[now] = ++Ts;
    for (int i = 1;(1 << i) <= dep[now];++i) f[now][i] = f[f[now][i-1]][i-1];
    for (int i = side[now];i;i = next[i])
        if (toit[i] != f[now][0])
            f[toit[i]][0] = now,dep[toit[i]] = dep[now]+1,dfs(toit[i]);
    R[now] = Ts;
}

inline void census(int ql,int qr,int pl,int pr,int &ll,int &rr)
{
    tot = 0;
    for (int i = pl;i <= pr;++i) path[i].ins2();
    for (int i = ql;i <= qr;++i) query[i].ins1();
    sort(bac+1,bac+tot+1);
    for (int i = 1;i <= tot;++i)
    {
        if (!bac[i].sign) sum[bac[i].be] = calc(bac[i].Y1);
        else modify(bac[i].Y1,-bac[i].sign),modify(bac[i].Y2+1,bac[i].sign);
    }
    for (int i = ql;i <= qr;++i)
    {
        if (sum[query[i].id] >= query[i].c) tmp[++ll] = query[i];
        else query[i].c -= sum[query[i].id],tmp[--rr] = query[i];
        sum[query[i].id] = 0;
    }
    for (int i = ql;i <= qr;++i) query[i] = tmp[i];
    for (int i = 1;i <= tot;++i)
        if (bac[i].sign) modify(bac[i].Y1,bac[i].sign),modify(bac[i].Y2+1,-bac[i].sign);
}

inline void work(int ql,int qr,int pl,int pr)
{
    if (ql > qr) return;
    if (pl == pr)
    {
        for (int i = ql;i <= qr;++i) ans[query[i].id] = path[pl].c;
        return;
    }
    int ll = ql-1,rr = qr+1,mid = (pl + pr)>>1;
    census(ql,qr,pl,mid,ll,rr);
    work(ql,ll,pl,mid); work(rr,qr,mid+1,pr);
}

int main()
{
    freopen("fruit.in","r",stdin);
    freopen("fruit.out","w",stdout);
    scanf("%d %d %d",&N,&P,&Q);
    for (int i = 1,a,b;i < N;++i) scanf("%d %d",&a,&b),ins(a,b);
    dfs(1);
    for (int i = 1;i <= P;++i) path[i].read(i),path[i].update(),path[i].change();
    sort(path+1,path+P+1);
    for (int i = 1;i <= Q;++i) query[i].read(i),query[i].update();
    work(1,Q,1,P);
    for (int i = 1;i <= Q;++i) printf("%d\n",ans[i]);
    fclose(stdin); fclose(stdout);
    return 0;
}

转载于:https://www.cnblogs.com/mmlz/p/4448719.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值