1. 程式人生 > >洛谷 P3242 [HNOI2015]接水果 解題報告

洛谷 P3242 [HNOI2015]接水果 解題報告

取消 區間 span 格式 type 有序 sum query 描述

P3242 [HNOI2015]接水果

題目描述

風見幽香非常喜歡玩一個叫做 \(osu!\) 的遊戲,其中她最喜歡玩的模式就是接水果。由於她已經\(DT\) \(FC\)\(\tt{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\) 的盤子,其中\(0\le c\le 10^9\)\(a\)不等於\(b\)

接下來\(Q\)行,每行三個數 \(u\)\(v\)\(k\),表示路徑為\(u\)\(v\)的水果,其中\(u\)不等於\(v\),你需要選擇第\(k\)小的盤子,第\(k\)小一定存在。

輸出格式:

對於每個果子,輸出一行表示選擇的盤子的權值。

說明

\(N,P,Q\le 40000\)


細節真多\(\tt{XD}\)

考慮如何處理路徑包含關系。

設被包含的路徑為\((u,v)\),要去包含\(\tt{Ta}\)的路徑為\((s,t)\),\(dfn_x\)\(low_x\)分別表示\(x\)\(dfs\)序和\(\tt{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+1\le dfn_s \le n\)\(dfn_v \le dfn_t \le low_v\)

    (這裏順序是個小細節)

  2. \(lca(u,v)\not=u\)

    則需要滿足\(dfn_v\le 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\n",ans[i]);
    return 0;
}

2018.11.4

洛谷 P3242 [HNOI2015]接水果 解題報告