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 eq b)。
接下来(Q)行,每行三个数(u,v,k),表示路径为(u)到(v)的水果,其中(u eq 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
#include
#include
#include
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>= 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
",ans[i]);fclose(stdin); fclose(stdout);return 0;
}