zoukankan      html  css  js  c++  java
  • bzoj4009: [HNOI2015]接水果

    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。

    题解:

    首先得到每个节点的dfs序和以这个节点为根的子树中dfs序最大值,分别记作dfn[u]和last[u]

    显然,A路径覆盖B路径当且仅当B路径的两个端点都在A路径上

    对于每一个盘子(u,v)(假设dfn[u]<dfn[v]),有以下两种情况

    1.lca(u,v)!=u时,如图

    (虚线表示有一条路径,实线表示一条边,下同)

    显然这个盘子能接到的水果(a,b)(假设dfn[a]<dfn[b],下同)满足dfn[u]<=dfn[a]<=last[u] && dfn[v]<=dfn[b]<=last[v]

    2.当lca(u,v)==u时,如图

    设w为u在(u,v)这条路径上的儿子节点

    则这个盘子能接到的水果(a,b)满足( (1<=a<=dfn[w]-1) || (last[w]+1<=a<=n) ) && (dfn[v]<=b<=last[v])

    我们可以将水果(a,b)看成平面上的一个点(dfn[a],dfn[b]),将盘子看成一个或两个矩形

    那么问题就转化为对于平面上的一个点,求覆盖它的第k小的矩形

    这是一个整体二分的经典题,用扫描线+树状数组搞定

    我们先将矩形按权值从小到大排序

    然后对于一个点,如果[l,mid]中能覆盖这个点的矩形数不小于k,则说明答案在[l,mid]中

    否则在[mid+1,r],同时k减去覆盖的矩形数

    code:

      1 #include<cstdio> 
      2 #include<iostream> 
      3 #include<cmath> 
      4 #include<cstring> 
      5 #include<algorithm> 
      6 #define maxn 80005 
      7 using namespace std; 
      8 char ch; 
      9 bool ok; 
     10 void read(int &x){ 
     11     for (ok=0,ch=getchar();!isdigit(ch);ch=getchar()) if (ch=='-') ok=1; 
     12     for (x=0;isdigit(ch);x=x*10+ch-'0',ch=getchar()); 
     13     if (ok) x=-x; 
     14 } 
     15 int n,p,q,a,b,u,v,w,val,lca,k,tot,now[maxn],son[maxn<<1],pre[maxn<<1]; 
     16 int idx,dfn[maxn],last[maxn],fa[maxn][17],dep[maxn]; 
     17 void put(int a,int b){pre[++tot]=now[a],now[a]=tot,son[tot]=b;} 
     18 void dfs(int u){ 
     19     dfn[u]=++idx; 
     20     for (int i=0;fa[u][i];i++) fa[u][i+1]=fa[fa[u][i]][i]; 
     21     for (int p=now[u],v=son[p];p;p=pre[p],v=son[p]) 
     22         if (v!=fa[u][0]) fa[v][0]=u,dep[v]=dep[u]+1,dfs(v); 
     23     last[u]=idx; 
     24 } 
     25 void swim(int &u,int d){for (int i=16;d;i--) if (d>=(1<<i)) d-=(1<<i),u=fa[u][i];} 
     26 int calc_lca(int u,int v){ 
     27     if (dep[u]<dep[v]) swap(u,v); 
     28     swim(u,dep[u]-dep[v]); 
     29     if (u==v) return u; 
     30     for (int i=16;i>=0;i--) if (fa[u][i]!=fa[v][i]) u=fa[u][i],v=fa[v][i]; 
     31     return fa[u][0]; 
     32 } 
     33 int cnt; 
     34 struct Plate{ 
     35     int x,xx,y,yy,val; 
     36 }plate[maxn]; 
     37 bool cmp1(Plate a,Plate b){return a.val<b.val;} 
     38 struct Point{ 
     39     int x,y,k,id; 
     40 }point[maxn],tmpl[maxn],tmpr[maxn]; 
     41 struct bit{ 
     42     #define lowbit(x) ((x)&(-(x))) 
     43     int v[maxn]; 
     44     void modify(int l,int r,int val){ 
     45         for (int i=l;i<=n;i+=lowbit(i)) v[i]+=val; 
     46         for (int i=r+1;i<=n;i+=lowbit(i)) v[i]-=val; 
     47     } 
     48     int query(int x){ 
     49         int ans=0; 
     50         for (int i=x;i;i-=lowbit(i)) ans+=v[i]; 
     51         return ans; 
     52     } 
     53 }T; 
     54 struct Event{ 
     55     int x,y,yy,v,id; 
     56 }event[maxn<<1]; 
     57 bool cmp2(Event a,Event b){ 
     58     if (a.x!=b.x) return a.x<b.x; 
     59     return a.id<b.id; 
     60 } 
     61 int sum[maxn],ans[maxn]; 
     62 void solve(int l,int r,int st,int ed){ 
     63     if (st>ed) return; 
     64     if (l==r){ 
     65         for (int i=st;i<=ed;i++) ans[point[i].id]=plate[l].val; 
     66         return; 
     67     } 
     68     int m=(l+r)>>1,siz=0; 
     69     for (int i=l;i<=m;i++){ 
     70         event[++siz]=(Event){plate[i].x,plate[i].y,plate[i].yy,1,0}; 
     71         event[++siz]=(Event){plate[i].xx,plate[i].y,plate[i].yy,-1,n+1}; 
     72     } 
     73     for (int i=st;i<=ed;i++) event[++siz]=(Event){point[i].x,point[i].y,0,0,i}; 
     74     sort(event+1,event+siz+1,cmp2); 
     75     for (int i=1;i<=siz;i++) if (st<=event[i].id&&event[i].id<=ed) sum[event[i].id]=T.query(event[i].y); 
     76     else T.modify(event[i].y,event[i].yy,event[i].v); 
     77     int a=0,b=0; 
     78     for (int i=st;i<=ed;i++) if (sum[i]>=point[i].k) tmpl[++a]=point[i]; 
     79     else tmpr[++b]=(Point){point[i].x,point[i].y,point[i].k-sum[i],point[i].id}; 
     80     for (int i=st;i<=st+a-1;i++) point[i]=tmpl[i-st+1]; 
     81     for (int i=st+a;i<=ed;i++) point[i]=tmpr[i-st-a+1]; 
     82     solve(l,m,st,st+a-1),solve(m+1,r,st+a,ed); 
     83 } 
     84 int main(){ 
     85     read(n),read(p),read(q); 
     86     for (int i=1;i<n;i++) read(a),read(b),put(a,b),put(b,a); 
     87     dfs(1); 
     88     for (int i=1;i<=p;i++){ 
     89         read(u),read(v),read(val),lca=calc_lca(u,v); 
     90         if (dfn[u]>dfn[v]) swap(u,v); 
     91         if (u!=lca) plate[++cnt]=(Plate){dfn[u],last[u],dfn[v],last[v],val}; 
     92         else{ 
     93             w=v,swim(w,dep[v]-dep[u]-1); 
     94             plate[++cnt]=(Plate){1,dfn[w]-1,dfn[v],last[v],val}; 
     95             if (last[w]+1<=n) plate[++cnt]=(Plate){dfn[v],last[v],last[w]+1,n,val}; 
     96         } 
     97     } 
     98     sort(plate+1,plate+cnt+1,cmp1); 
     99     for (int i=1;i<=q;i++){ 
    100         read(u),read(v),read(k); 
    101         if (dfn[u]>dfn[v]) swap(u,v); 
    102         point[i]=(Point){dfn[u],dfn[v],k,i}; 
    103     } 
    104     solve(1,cnt,1,q); 
    105     for (int i=1;i<=q;i++) printf("%d
    ",ans[i]); 
    106     return 0; 
    107 }
  • 相关阅读:
    【技术贴】每次打开excel表格都会弹出新excel。book1.xls解决方法
    【技术贴】鼠标右键盘符属性报错Volume filter WMI not found的解决办法
    解决QQ聊天QQ秀咒语为什么我不能施放咒语/看不到咒语效果?
    【java】servlet输出pdf文件到浏览器 教程
    C#抽象工厂模式的几种实现方法及比较(外摘)
    SQL 语句汇总With子句
    浅析.NET开发中代理模式的使用(外摘)
    使用设计模式构建通用数据库访问类(外摘)
    Cognos产品组件及各组件功能介绍
    游标Oracle游标汇总
  • 原文地址:https://www.cnblogs.com/chenyushuo/p/5056663.html
Copyright © 2011-2022 走看看