• 洛谷 P3242 [HNOI2015]接水果 解题报告


    P3242 [HNOI2015]接水果

    题目描述

    风见幽香非常喜欢玩一个叫做 (osu!) 的游戏,其中她最喜欢玩的模式就是接水果。由于她已经(DT) (FC)( t{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) 小的那个盘子,每个盘子可重复使用(没有使用次数的上限:一个盘子接完一个水果后,后面还可继续接其他水果,只要它是水果路径的子路径)。幽香认为这个游戏很难,你能轻松解决给她看吗?

    输入输出格式

    输入格式:

    第一行三个数 (n)(P)(Q),表示树的大小和盘子的个数和水果的个数。

    接下来(n-1) 行,每行两个数 (a)(b),表示树上的(a)(b)之间有一条边。树中顶点按(1)(n)标号。

    接下来 (P) 行,每行三个数 (a)(b)(c),表示路径为 (a)(b)、权值为 (c) 的盘子,其中(0le cle 10^9)(a)不等于(b)

    接下来(Q)行,每行三个数 (u)(v)(k),表示路径为(u)(v)的水果,其中(u)不等于(v),你需要选择第(k)小的盘子,第(k)小一定存在。

    输出格式:

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

    说明

    (N,P,Qle 40000)


    细节真多( t{XD})

    考虑如何处理路径包含关系。

    设被包含的路径为((u,v)),要去包含( t{Ta})的路径为((s,t)),(dfn_x)(low_x)分别表示(x)(dfs)序和( t{Ta})子树的(dfs)序末尾。

    这里需要维护路径二元组的有序性,我们令前一维的点的(dfs)序更小。

    对被包含的路径进行分类讨论

    1. (lca(u,v)=u)

      (w)(u)的第一个儿子且(w)(v)的祖先

      那么若满足包含关系,需满足

      (dfn_v le dfn_t le low_v)(1 le dfs_s le dfn_w -1)

      或者(low_w+1le dfn_s le n)(dfn_v le dfn_t le low_v)

      (这里顺序是个小细节)

    2. (lca(u,v) ot=u)

      则需要满足(dfn_vle dfn_s le low_v)(dfn_u le dfn_t le low_t)

    发现可以把限制条件转换到二维数点之类的问题。

    具体的,可以把被包含的路径(盘子)转换成矩形,包含别人的路径(水果)转换成点。

    那么问题就转化成了包含某个点的矩形的第(k)小值。

    我们可以整体二分,内部的问题是某个点被多少个矩形包含,可以使用扫描线求解。

    这里扫描线差分一下用树状数组就可以了。


    Code:

    #include <cstdio>
    #include <cctype>
    #include <algorithm>
    const int N=40010;
    int Next[N<<1],to[N<<1],head[N],cnt;
    void add(int u,int v)
    {
        to[++cnt]=v,Next[cnt]=head[u],head[u]=cnt;
    }
    int read()
    {
        char c=getchar();int x=0;
        while(!isdigit(c)) c=getchar();
        while(isdigit(c)) {x=x*10+c-'0';c=getchar();}
        return x;
    }
    int dep[N],f[N][17],dfn[N],low[N],dfsclock;
    int n,m,Q,ans[N];
    void dfs(int now)
    {
        dfn[now]=++dfsclock;
        for(int i=1;f[now][i-1];i++) f[now][i]=f[f[now][i-1]][i-1];
        for(int i=head[now];i;i=Next[i])
        {
            int v=to[i];
            if(v==f[now][0]) continue;
            dep[v]=dep[now]+1;
            f[v][0]=now;
            dfs(v);
        }
        low[now]=dfsclock;
    }
    int LCA(int x,int y)
    {
        if(dep[x]<dep[y]) return LCA(y,x);
        for(int i=16;~i;i--)
            if(dep[f[x][i]]>=dep[y])
                x=f[x][i];
        if(x==y) return x;
        for(int i=16;~i;i--)
            if(f[x][i]!=f[y][i])
                x=f[x][i],y=f[y][i];
        return f[x][0];
    }
    int get(int x,int y)
    {
        for(int i=16;~i;i--)
            if(dep[f[x][i]]>dep[y])
                x=f[x][i];
        return x;
    }
    struct node
    {
        int op,x,l,r,k,d;//盘子,大的做x,区间,权值,线的性质
        //第几个水果,横,纵,留空,第k小,留空
        node(){}
        node(int op,int x,int l,int r,int k,int d){this->op=op,this->x=x,this->l=l,this->r=r,this->k=k,this->d=d;}
        bool friend operator <(node n1,node n2)
        {
            if(n1.x==n2.x)//先盘子,再水果,盘子中先取消失线
                return n1.op==n2.op?n1.d<n2.d:n1.op<n2.op;
            return n1.x<n2.x;
        }
    }q[N<<3],ql[N<<3],qr[N<<3];
    int S[N],tmp;
    void Swap(int &x,int &y){tmp=x,x=y,y=tmp;}
    void change(int x,int d){while(x<=n)S[x]+=d,x+=x&-x;}
    int query(int x){int sum=0;while(x)sum+=S[x],x-=x&-x;return sum;}
    #define rep(i,a,b) for(int i=a;i<=b;i++)
    void divide(int l,int r,int s,int t)
    {
        if(s>t) return;
        if(l==r){rep(i,s,t)ans[q[i].op]=l;return;}
        int mid=l+r>>1,lp=0,rp=0;
        rep(i,s,t)
        {
            if(q[i].op)
            {
                int c=query(q[i].l);
                if(c>=q[i].k) ql[++lp]=q[i];
                else qr[++rp]=q[i],qr[rp].k-=c;
            }
            else
            {
                if(q[i].k<=mid) change(q[i].l,q[i].d),change(q[i].r+1,-q[i].d),ql[++lp]=q[i];
                else qr[++rp]=q[i];
            }
        }
        rep(i,s,t) if(!q[i].op&&q[i].k<=mid) change(q[i].l,-q[i].d),change(q[i].r+1,q[i].d);
        rep(i,s,s+lp-1) q[i]=ql[i+1-s];
        rep(i,s+lp,t) q[i]=qr[i+1-s-lp];
        divide(l,mid,s,s+lp-1),divide(mid+1,r,s+lp,t);
    }
    int main()
    {
        n=read(),m=read(),Q=read();
        int u,v,k,w,lca,t=0;rep(i,1,(n-1))u=read(),v=read(),add(u,v),add(v,u);
        dep[1]=1,dfs(1);
        rep(i,1,m)
        {
            u=read(),v=read(),k=read();
            if(dfn[u]>dfn[v]) Swap(u,v);
            lca=LCA(u,v);
            if(lca==u)
            {
                w=get(v,u);
                q[++t]=node(0,1,dfn[v],low[v],k,1);
                q[++t]=node(0,dfn[w],dfn[v],low[v],k,-1);
                q[++t]=node(0,dfn[v],low[w]+1,n,k,1);
                q[++t]=node(0,low[v]+1,low[w]+1,n,k,-1);
            }
            else
            {
                q[++t]=node(0,dfn[u],dfn[v],low[v],k,1);
                q[++t]=node(0,low[u]+1,dfn[v],low[v],k,-1);
            }
        }
        rep(i,1,Q)
        {
            q[++t].x=dfn[read()],q[t].l=dfn[read()],q[t].k=read(),q[t].op=i;
            if(q[t].x>q[t].l) Swap(q[t].x,q[t].l);
        }
        std::sort(q+1,q+1+t);
        divide(0,(int)(1e9),1,t);
        rep(i,1,Q) printf("%d
    ",ans[i]);
        return 0;
    }
    

    2018.11.4

  • 相关阅读:
    软件设计图工具
    属性读取
    socket ReceiveAsync
    Type.GetType()跨程序集反射
    实例化类的时候代码运行顺序
    C# 互斥对象--Mutex---线程同步
    【vim】vim配置教程+源码
    【框架】SPI四种模式+通用设备驱动实现
    【网络】NFS网络文件系统
    【C语言】函数不定长参数
  • 原文地址:https://www.cnblogs.com/butterflydew/p/9905429.html
Copyright © 2020-2023  润新知