4009: [HNOI2015]接水果

18 篇文章 0 订阅
12 篇文章 0 订阅

4009: [HNOI2015]接水果

Time Limit: 60 Sec   Memory Limit: 512 MB
Submit: 623   Solved: 293
[ Submit][ Status][ Discuss]

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。 

Source

[ Submit][ Status][ Discuss]

考虑每个盘子可以接到哪些水果,借此分类
记第i个盘子是路径(ai,bi),调整成dfn[ai] < dfn[bi],找出它们的LCA,记作lca
对于每个水果的路径,也调整成(ai,bi),dfn[ai] < dfn[bi]的形式
如果lca != ai,设水果(Ai,Bi),只要dfn[Ai]∈[dfn[ai],last[ai]]且dfn[Bi]∈[dfn[bi],last[bi]],就能接到了
如果lca == ai,就要dfn[Ai]∈[dfn[bi],last[bi]],对于Bi,则在dfs序上被剖成两段
这样,每个盘子能接到的水果可以转换成二维平面上的一个或两个矩形
每个水果也能转换成二维平面上的一个点
问题就变成了,给出二维平面上的一个点集和一些矩形,
问对于每个点,能覆盖它的矩形中权值第k大的权值是多少
就用扫描线 + CDQ分治解决,复杂度O(nlog^2)
CDQ分治权值,用线段树套树状数组维护
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
 
const int maxn = 4E4 + 40;
const int T = 18;
const int G = 4;
 
struct Modify{
    int l,r,w,typ; Modify(){}
    Modify(int l,int r,int w,int typ): l(l),r(r),w(w),typ(typ){}
};
 
struct Query{
    int pos,k,Num; Query(){}
    Query(int pos,int k,int Num): pos(pos),k(k),Num(Num){}
    bool operator < (const Query &B) const {return pos < B.pos;}
};
 
int n,m,q,dfs_clock,cw,W[maxn],w[maxn],A[maxn],B[maxn],dfn[maxn],last[maxn],L[maxn],Ans[maxn],fa[maxn][T],siz[maxn*G],res[maxn];
 
vector <int> v[maxn],Sum[maxn*G],C[maxn*G];
vector <Modify> M[maxn];
vector <Query> Q[maxn];
 
int getint()
{
    char ch = getchar(); int ret = 0;
    while (ch < '0' || '9' < ch) ch = getchar();
    while ('0' <= ch && ch <= '9')
        ret = ret * 10 + ch - '0',ch = getchar();
    return ret;
}
 
void Dfs(int x,int from)
{
    dfn[x] = ++dfs_clock;
    for (int i = 1; i < T; i++) fa[x][i] = fa[fa[x][i-1]][i-1];
    for (int i = 0; i < v[x].size(); i++)
    {
        int to = v[x][i];
        if (to == from) continue;
        L[to] = L[x] + 1; fa[to][0] = x; Dfs(to,x);
    }
    last[x] = dfs_clock;
}
 
void Insert(int o,int l,int r,int L,int R,int k)
{
    C[o].push_back(L); C[o].push_back(R);
    if (l == r) return; int mid = (l + r) >> 1;
    if (k <= mid) Insert(o<<1,l,mid,L,R,k);
    else Insert(o<<1|1,mid+1,r,L,R,k);
}
 
void Build_Tree(int o,int l,int r)
{
    C[o].push_back(0); C[o].push_back(maxn);
    sort(C[o].begin(),C[o].end()); siz[o] = 1;
    for (int i = 2; i < C[o].size(); i++)
        if (C[o][i] != C[o][i-1]) C[o][++siz[o]] = C[o][i];
    for (int i = 0; i <= siz[o]; i++) Sum[o].push_back(0);
    while (C[o].size() - 1 > siz[o]) C[o].pop_back();
    if (l == r) return; int mid = (l + r) >> 1;
    Build_Tree(o<<1,l,mid); Build_Tree(o<<1|1,mid+1,r);
}
 
void modify(int o,int l,int r,vector <Modify> &g)
{
    vector <int> &c = C[o];
    for (int i = 0; i < g.size(); i++)
    {
        int l = lower_bound(c.begin(),c.end(),g[i].l) - c.begin();
        int r = lower_bound(c.begin(),c.end(),g[i].r) - c.begin();
        int k = g[i].typ;
        for (int j = l; j <= siz[o]; j += j&-j) Sum[o][j] += k;
        for (int j = r; j <= siz[o]; j += j&-j) Sum[o][j] -= k;
    }
    if (l == r) return; int mid = (l + r) >> 1;
    vector <Modify> g1,g2; g1.clear(); g2.clear();
    for (int i = 0; i < g.size(); i++)
        if (g[i].w <= mid) g1.push_back(g[i]); else g2.push_back(g[i]);
    if (g1.size()) modify(o<<1,l,mid,g1);
    if (g2.size()) modify(o<<1|1,mid+1,r,g2);
}
 
void query(int o,int l,int r,vector <Query> &h)
{
    if (l == r)
    {
        for (int i = 0; i < h.size(); i++)
            Ans[h[i].Num] = l;
        return;
    }
    int mid = (l + r) >> 1;
    vector <Query> h1,h2; h1.clear(); h2.clear();
    vector <int> &sum = Sum[o << 1],&c = C[o << 1];
    for (int i = 0; i < h.size(); i++)
    {
        int pos = lower_bound(c.begin(),c.end(),h[i].pos) - c.begin();
        if (c[pos] > h[i].pos) --pos; int tot = 0;
        for (int j = pos; j > 0; j -= j&-j) tot += sum[j];
        if (tot + res[h[i].Num] >= h[i].k) h1.push_back(h[i]);
        else res[h[i].Num] += tot,h2.push_back(h[i]);
    }
    if (h1.size()) query(o<<1,l,mid,h1);
    if (h2.size()) query(o<<1|1,mid+1,r,h2);
}
 
int LCA(int p,int q)
{
    if (L[p] < L[q]) swap(p,q);
    for (int i = T - 1; i >= 0; i--)
        if (L[p] - (1 << i) >= L[q]) p = fa[p][i];
    if (p == q) return p;
    for (int i = T - 1; i >= 0; i--)
        if (fa[p][i] != fa[q][i]) p = fa[p][i],q = fa[q][i];
    return fa[p][0];
}
 
int Quickfa(int x,int y)
{
    for (int now = 0; y; y >>= 1,now++)
        if (y & 1) x = fa[x][now];
    return x;
}
 
int main()
{
    #ifdef DMC
        freopen("DMC.txt","r",stdin);
    #endif
     
    n = getint(); m = getint(); q = getint();
    for (int i = 1; i < n; i++)
    {
        int x = getint(),y = getint();
        v[x].push_back(y);
        v[y].push_back(x);
    }
    L[1] = 1; Dfs(1,0);
    for (int i = 1; i <= m; i++)
    {
        A[i] = getint(); B[i] = getint(); W[i] = w[i] = getint();
        if (dfn[A[i]] > dfn[B[i]]) swap(A[i],B[i]);
    }
    sort(W + 1,W + m + 1); cw = 1;
    for (int i = 2; i <= m; i++)
        if (W[i] != W[i-1]) W[++cw] = W[i];
    for (int i = 1; i <= m; i++)
    {
        int lca = LCA(A[i],B[i]);
        w[i] = lower_bound(W + 1,W + cw + 1,w[i]) - W;
        if (lca != A[i])
        {
            Insert(1,1,cw,dfn[A[i]],last[A[i]] + 1,w[i]);
            M[dfn[B[i]]].push_back(Modify(dfn[A[i]],last[A[i]] + 1,w[i],1));
            M[last[B[i]] + 1].push_back(Modify(dfn[A[i]],last[A[i]] + 1,w[i],-1));
        }
        else
        {
            int z = Quickfa(B[i],L[B[i]] - L[A[i]] - 1);
            if (dfn[z] > 1)
            {
                Insert(1,1,cw,1,dfn[z],w[i]);
                M[dfn[B[i]]].push_back(Modify(1,dfn[z],w[i],1));
                M[last[B[i]] + 1].push_back(Modify(1,dfn[z],w[i],-1));
            }
            if (last[z] < n)
            {
                Insert(1,1,cw,dfn[B[i]],last[B[i]] + 1,w[i]);
                M[last[z] + 1].push_back(Modify(dfn[B[i]],last[B[i]] + 1,w[i],1));
                M[n + 1].push_back(Modify(dfn[B[i]],last[B[i]] + 1,w[i],-1));
            }
        }
    }
    for (int i = 1; i <= q; i++)
    {
        int x = getint(),y,k;
        y = getint(); k = getint();
        if (dfn[x] > dfn[y]) swap(x,y);
        Q[dfn[y]].push_back(Query(dfn[x],k,i));
    }
    Build_Tree(1,1,cw);
     
    for (int i = 1; i <= n; i++)
    {
        if (M[i].size()) modify(1,1,cw,M[i]);
        if (Q[i].size()) query(1,1,cw,Q[i]);
    }
    for (int i = 1; i <= q; i++) printf("%d\n",W[Ans[i]]);
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值