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

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。

解题思路:

考虑一个盘子能接到什么样的水果。
对于一条路径x-y:

若x与y不成祖先-孩子关系,则包含它的路径u-v满足:起点在x的子树里,且终点在y的子树里。
即in[x]<=dfn[u]<=out[x],in[y]<=dfn[v]<=out[y]

若x与y成祖先-孩子关系,假设y是x的祖先,z是y到x方向的第一个节点,则包含它的路径u-v满足:起点在x的子树里,且终点不在z的子树里。
即in[y]<=dfn[u]<=out[u],1<=dfn[v]<=in[z]-1或out[z]+1<=dfn[v]<=n

于是上述条件相当于将每个盘子拆成一个或两个矩形,每个水果可以当成一个点,相当于要求覆盖该点的第k小矩形。

注意中u,v满足的子树区间都是不相交的,所以两种情况作为横坐标的都可以取dfs序较小的那一个,纵坐标取dfs序较大的一个,这样就避免麻烦的讨论了。

接下来上整体二分+扫描线即可。

#include<bits/stdc++.h>
using namespace std;

int getint()
{
    int i=0,f=1;char c;
    for(c=getchar();(c!='-')&&(c<'0'||c>'9');c=getchar());
    if(c=='-')f=-1,c=getchar();
    for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
    return i*f;
}

const int N=40005,INF=1e9;
int n,m,Q,cnt,ans[N],val[N],bit[N];
int tot,first[N],nxt[N<<1],to[N<<1];
int idx,in[N],out[N],dep[N],fa[N][20];
struct matrix
{
    int x1,x2,y1,y2,val;
    matrix(){}
    matrix(int _x1,int _x2,int _y1,int _y2,int _val):x1(_x1),x2(_x2),y1(_y1),y2(_y2),val(_val){}
}a[N<<1],b[N<<2];
inline bool cmp1(const matrix &a,const matrix &b){return a.val<b.val;}
inline bool cmp2(const matrix &a,const matrix &b){return a.x1<b.x1;}
struct node{int x,y,id,k;}q[N],t[N];
inline bool cmp(const node &a,const node &b){return a.x<b.x;}

void add(int x,int y)
{
    nxt[++tot]=first[x],first[x]=tot,to[tot]=y;
}

void dfs(int u)
{
    in[u]=++idx;
    for(int i=1;i<20;i++)fa[u][i]=fa[fa[u][i-1]][i-1];
    for(int e=first[u];e;e=nxt[e])
    {
        int v=to[e];
        if(v==fa[u][0])continue;
        fa[v][0]=u,dep[v]=dep[u]+1;
        dfs(v);
    }
    out[u]=idx;
}

int find(int x,int y)
{
    int det=dep[x]-dep[y]-1;
    for(int i=0;(1<<i)<=det;i++)
        if(det&(1<<i))x=fa[x][i];
    return x;
}

void insert(int x,int v)
{
    for(int i=x;i<=n;i+=i&(-i))bit[i]+=v;
}

int query(int x)
{
    int res=0;
    for(int i=x;i;i-=i&(-i))res+=bit[i];
    return res;
}

void calc(int ql,int qr,int vl,int vr)
{
    for(int i=ql;i<=qr;i++)val[i]=0;
    int l=1,r=cnt,num=0;
    while(l<=r)
    {
        int mid=l+r>>1;
        if(a[mid].val<vl)l=mid+1;
        else r=mid-1;
    }
    if(l>cnt)return;
    for(int i=l;a[i].val<=vr&&i<=cnt;i++)
    {
        b[++num]=matrix(a[i].x1,0,a[i].y1,a[i].y2,1);
        b[++num]=matrix(a[i].x2+1,0,a[i].y1,a[i].y2,-1);
    }
    if(!num)return;
    sort(b+1,b+num+1,cmp2),sort(q+ql,q+qr+1,cmp);
    for(int i=1,j=ql;i<=num||j<=qr;)
    {
        int x;
        if(j>qr)x=b[i].x1;
        else if(i>num)x=q[j].x;
        else x=min(b[i].x1,q[j].x);
        while(i<=num&&b[i].x1==x)
            insert(b[i].y1,b[i].val),insert(b[i].y2+1,-b[i].val),i++;
        while(j<=qr&&q[j].x<=x)val[j]=query(q[j].y),j++;
    }
}

void solve(int ql,int qr,int vl,int vr)
{
    if(ql>qr)return;
    if(vl==vr)
    {
        for(int i=ql;i<=qr;i++)ans[q[i].id]=vl;
        return;
    }
    int vmid=(vl+vr)>>1;
    calc(ql,qr,vl,vmid);
    int st=ql-1;
    for(int i=ql;i<=qr;i++)if(q[i].k<=val[i])t[++st]=q[i];
    int qmid=st;
    for(int i=ql;i<=qr;i++)if(q[i].k>val[i])q[i].k-=val[i],t[++st]=q[i];
    for(int i=ql;i<=qr;i++)q[i]=t[i];
    solve(ql,qmid,vl,vmid),solve(qmid+1,qr,vmid+1,vr);
}

int main()
{
    //freopen("lx.in","r",stdin);
    n=getint(),m=getint(),Q=getint();
    int x,y,z;
    for(int i=1;i<n;i++)
    {
        x=getint(),y=getint();
        add(x,y),add(y,x);
    }
    dfs(1);
    for(int i=1;i<=m;i++)
    {
        x=getint(),y=getint(),z=getint();
        if(in[x]>in[y])swap(x,y);
        if(in[x]<=in[y]&&out[y]<=out[x])
        {
            int w=find(y,x);
            if(in[w]>1)a[++cnt]=matrix(1,in[w]-1,in[y],out[y],z);
            if(out[w]<n)a[++cnt]=matrix(in[y],out[y],out[w]+1,n,z);
        }
        else a[++cnt]=matrix(in[x],out[x],in[y],out[y],z);
    }
    sort(a+1,a+cnt+1,cmp1);
    for(int i=1;i<=Q;i++)
    {
        x=getint(),y=getint(),z=getint();
        if(in[x]>in[y])swap(x,y);
        q[i].x=in[x],q[i].y=in[y],q[i].id=i,q[i].k=z;
    }
    solve(1,Q,0,INF);
    for(int i=1;i<=Q;i++)printf("%d\n",ans[i]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值