1. 程式人生 > >接水果(fruit)——整體二分+掃描線

接水果(fruit)——整體二分+掃描線

The 等於 範圍 包含關系 解決 會有 判斷 pub ans

題目

【題目描述】

風見幽香非常喜歡玩一個叫做 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$ 小的那個盤子,每個盤子可重復使用(沒有使用次數的上限:一個盤子接完一個水果後,後面還可繼續接其他水果,只要它是水果路徑的子路徑)。幽香認為這個遊戲很難,你能輕松解決給她看嗎?

【輸入格式】

第一行三個數 $n$ 和 $P$ 和 $Q$,表示樹的大小和盤子的個數和水果的個數。

接下來 $n-1$ 行,每行兩個數 $a$、$b$,表示樹上的 $a$ 和 $b$ 之間有一條邊。樹中頂點按 $1$ 到 $n$ 標號。 接下來 $P$ 行,每行三個數 $a$、$b$、$c$,表示路徑為 $a$ 到 $b$、權值為 $c$ 的盤子,其中 $0 \leq c \leq 10^9, \ a \neq b$。

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

【輸出格式】

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

【樣例輸入】

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

【樣例輸出】

442139372
333042360
442139372
283254485
283254485
217394434
217394434
217394434
217394434
217394434

【數據範圍與提示】

對於所有數據,$N,P,Q \leq 40000$。

題解

考慮一個盤子 $ u,v $ 能接到的水果的範圍,那麽水果的 $ s,t $ 必須分別屬於盤子兩個的兩個子樹,問題即轉化成 $ s,t $ 屬於的第 $k$ 大的盤子

因為一個子樹在 dfs 序上是一個區間,一個盤子就是兩個區間的包含關系,那麽映射到坐標系上盤子就成了一個矩形,水果就成了一個點,問題變成覆蓋某一個點的第 $k$ 大的矩形

考慮如何解決,整體二分矩形,判斷覆蓋個數是否大於,然後左右遞歸即可

至於判斷,類似掃描線的思想,將矩形按一維排序,另一維區間用數據結構(樹狀數組)維護,單點查詢即可

如果水果 $ u,v $ 的 lca 為 $ u $ 或 $ v $ 時,需要特判一下

時間效率:$ O(n \log^2 n) $

代碼

技術分享圖片
 1 #include<bits/stdc++.h>
 2 const int N=8e4+10;
 3 using namespace std;
 4 int n,m,q,tot,fa[N][18],dfn[N],tim,last[N],dep[N],ans[N],sum[N],head[N],cnt;
 5 struct edge{int to,nex;}e[N<<1];
 6 struct Plate{int x1,x2,y1,y2,v;}pla[N];
 7 struct node{int x,y1,y2,v,id;}eve[N];
 8 struct fruit{int x,y,k,id;}fru[N],s1[N],s2[N];
 9 bool cmp(Plate a,Plate b){return a.v<b.v;}
10 bool cmp1(node a,node b){return a.x==b.x?a.id<b.id:a.x<b.x;}
11 void add(int s,int t){e[++cnt]=(edge){t,head[s]},head[s]=cnt;}
12 
13 class Bit{
14 public:
15     int val[N];
16     void modify(int l,int r,int v){
17         for (int i=l;i<=n;i+=(i&(-i))) val[i]+=v;
18         for (int i=r+1;i<=n;i+=(i&(-i))) val[i]-=v;
19     }
20     int query(int x){int res=0;for (;x;x-=(x&(-x))) res+=val[x];return res;}
21 }T;
22  
23 void dfs(int x){
24     dfn[x]=++tim;
25     for (int i=0;fa[x][i];i++) fa[x][i+1]=fa[fa[x][i]][i];
26     for (int k=head[x],v;k;k=e[k].nex)
27         if ((v=e[k].to)!=fa[x][0])
28             fa[v][0]=x,dep[v]=dep[x]+1,dfs(v);
29     last[x]=tim;
30 }
31 int jump(int a,int h){for (int i=16;h;i--) if (h>=(1<<i)) h-=(1<<i),a=fa[a][i];return a;}
32 int lca(int a,int b){
33     if (dep[a]<dep[b]) swap(a,b);
34     a=jump(a,dep[a]-dep[b]);
35     if (a==b) return a;
36     for (int i=16;i>=0;i--) if (fa[a][i]!=fa[b][i]) a=fa[a][i],b=fa[b][i];
37     return fa[a][0];
38 }
39  
40 void solve(int l,int r,int st,int ed){
41     if (st>ed) return; 
42     if (l==r){
43         for (int i=st;i<=ed;i++) ans[fru[i].id]=pla[l].v;
44         return;
45     }
46     int mid=(l+r)>>1,siz=0;
47     for (int i=l;i<=mid;i++){
48         eve[++siz]=(node){pla[i].x1,pla[i].y1,pla[i].y2,1,0};
49         eve[++siz]=(node){pla[i].x2,pla[i].y1,pla[i].y2,-1,n+1};
50     }
51     for (int i=st;i<=ed;i++) eve[++siz]=(node){fru[i].x,fru[i].y,0,0,i};
52     sort(eve+1,eve+1+siz,cmp1);
53     for (int i=1;i<=siz;i++) 
54         if (st<=eve[i].id&&eve[i].id<=ed) sum[eve[i].id]=T.query(eve[i].y1);
55         else T.modify(eve[i].y1,eve[i].y2,eve[i].v);
56     int a=0,b=0;
57     for (int i=st;i<=ed;i++) {
58         if (sum[i]>=fru[i].k) s1[++a]=fru[i];
59         else s2[++b]=(fruit){fru[i].x,fru[i].y,fru[i].k-sum[i],fru[i].id};
60     }
61     for (int i=st;i<=st+a-1;i++) fru[i]=s1[i-st+1];
62     for (int i=st+a;i<=ed;i++) fru[i]=s2[i-st-a+1];
63     solve(l,mid,st,st+a-1),solve(mid+1,r,st+a,ed);
64 }
65  
66 int main(){
67     scanf("%d%d%d",&n,&m,&q);
68     for (int i=1,a,b;i<n;i++) scanf("%d%d",&a,&b),add(a,b),add(b,a);
69     dfs(1);
70     for (int i=1,a,b,c,u;i<=m;i++){
71         scanf("%d%d%d",&a,&b,&c);u=lca(a,b);
72         if (dfn[a]>dfn[b]) swap(a,b);
73         if (u!=a) pla[++tot]=(Plate){dfn[a],last[a],dfn[b],last[b],c};
74         else{
75             int w=jump(b,dep[b]-dep[a]-1);
76             pla[++tot]=(Plate){1,dfn[w]-1,dfn[b],last[b],c};
77             if (last[w]<n) pla[++tot]=(Plate){dfn[b],last[b],last[w]+1,n,c};
78         }
79     }
80     sort(pla+1,pla+1+tot,cmp);
81     for (int i=1,a,b,k;i<=q;i++){
82         scanf("%d%d%d",&a,&b,&k);
83         if (dfn[a]>dfn[b]) swap(a,b);
84         fru[i]=(fruit){dfn[a],dfn[b],k,i};
85     }
86     solve(1,tot,1,q);
87     for (int i=1;i<=q;i++) printf("%d\n",ans[i]);
88     return 0;
89 }
View Code

接水果(fruit)——整體二分+掃描線