BZOJ4009 & 洛谷3242 & LOJ2113:[HNOI2015]接水果——题解

https://www.lydsy.com/JudgeOnline/problem.php?id=4009

https://www.luogu.org/problemnew/show/P3242

https://loj.ac/problem/2113

风见幽香非常喜欢玩一个叫做 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 小的那个盘子,每个盘子可重复使用(没有使用次数的上限:一个盘子接完一个水果后,后面还可继续接其他水果,只要它是水果路径的子路径)。幽香认为这个游戏很难,你能轻松解决给她看吗?

因为预先做过LOJ6276:果树,不难想到对于树上路径的处理方法和它一样。

那么对于每个盘子用dfs序表示的路径点(u,v),分两种情况:

1.u和v有一个不同于二者的lca

显然它能接到的水果的两端一个在u的子树中,一个在v的子树中。

2.v是u的祖先。

显然它能接到的水果的两端一个在u的子树中,一个在v的子树的补集(包括v)中。

那么对于一个水果的路径点,如果在这个矩形当中,就说明这个水果能够被哪些盘子所接。

那么处理完之后,显然不能用主席树来解决第k大(如果能当我没说),于是我们整体二分一下即可。

……这是我最开始做这道题的想法,但是码了4h后对此绝望,对着数据参着题解调到现在才过。

说几个(我)容易错的点:

1.区间第k小,所以不是第k大的做法,注意答案在左区间还是右区间。

2.用扫描线存的时候注意空间开大点,另外上边界不要忘了+1

代码138行凑和吧,但是细节真心多。

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=4e4+5;
const int B=17;
inline int read(){
    int X=0,w=0;char ch=0;
    while(!isdigit(ch)){w|=ch==‘-‘;ch=getchar();}
    while(isdigit(ch))X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    return w?-X:X;
}
struct path{
    int to,nxt;
}e[N*2];
struct dish{
    int x1,x2,y,c,w;
}d[4*N],td1[4*N],td2[4*N];
struct fruit{
    int x,y,k,id;
}f[N],tf1[N],tf2[N];
int m,b[N],ans[N],tr[N],c[N];
int anc[N][B+4],dep[N],size[N];
int n,p,q,cnt,head[N],pos[N],tot;
inline bool cmp1(dish a,dish b){
    return a.y<b.y;
}
inline bool cmp2(fruit a,fruit b){
    return a.y<b.y;
}
inline void add(int u,int v){
    e[++cnt].to=v;e[cnt].nxt=head[u];head[u]=cnt;
}
void dfs(int u){
    pos[u]=++tot;size[u]=1;
    dep[u]=dep[anc[u][0]]+1;
    for(int i=1;i<=B;++i)
        anc[u][i]=anc[anc[u][i-1]][i-1];
    for(int i=head[u];i;i=e[i].nxt){
        int v=e[i].to;
        if(v!=anc[u][0]){
            anc[v][0]=u;
            dfs(v);
            size[u]+=size[v];
        }
    }
}
inline int LCA(int i,int j){
    if(dep[i]<dep[j])swap(i,j);
    for(int k=B;k>=0;--k)
        if(dep[anc[i][k]]>=dep[j])i=anc[i][k];
    if(i==j)return i;
    for(int k=B;k>=0;--k)
        if(anc[i][k]!=anc[j][k])
            i=anc[i][k],j=anc[j][k];
    return anc[i][0];
}
inline int lowbit(int t){return t&(-t);}
inline void ins(int x,int y){
    for(int i=x;i<=n;i+=lowbit(i))tr[i]+=y;
}
inline int qry(int x){
    int res=0;
    for(int i=x;i;i-=lowbit(i))res+=tr[i];
    return res;
}
inline void mdy(int l,int r,int w){
    ins(l,w);ins(r+1,-w);
}
void solve(int L,int R,int s,int t,int l,int r){
    if(L>R||s>t)return;
    if(l==r){
        for(int i=L;i<=R;++i)ans[f[i].id]=c[l];
        return;
    }
    int id1=0,id2=0,if1=0,if2=0,mid=(l+r)>>1,j=s;
    for(int i=L;i<=R;++i){
        for(;j<=t&&d[j].y<=f[i].y;++j){
            if(d[j].c>c[mid])td1[id1++]=d[j];
            else mdy(d[j].x1,d[j].x2,d[j].w),td2[id2++]=d[j];
        }
        int tmp=qry(f[i].x);
        if(f[i].k>tmp)f[i].k-=tmp,tf1[if1++]=f[i];
        else tf2[if2++]=f[i];
    }
    for(;j<=t;++j){
        if(d[j].c>c[mid])td1[id1++]=d[j];
        else mdy(d[j].x1,d[j].x2,d[j].w),td2[id2++]=d[j];
    }
    int mdst=s+id1,MID=L+if1;
    for(int i=s;i<mdst;++i)d[i]=td1[i-s];
    for(int i=mdst;i<=t;++i)d[i]=td2[i-mdst];
    for(int i=L;i<MID;++i)f[i]=tf1[i-L];
    for(int i=MID;i<=R;++i)f[i]=tf2[i-MID];
    solve(L,MID-1,s,mdst-1,mid+1,r);solve(MID,R,mdst,t,l,mid);
    return;
}
int main(){
    n=read(),p=read(),q=read();
    for(int i=1;i<n;++i){
        int u=read(),v=read();
        add(u,v);add(v,u);
    }
    dfs(1);
    for(int i=1;i<=p;++i){
        int u=read(),v=read();c[i]=read();
        if(pos[u]>pos[v])swap(u,v);
        int lca=LCA(u,v);
        if(lca!=u&&lca!=v){
            d[++m]=(dish){pos[u],pos[u]+size[u]-1,pos[v],c[i],1};
            d[++m]=(dish){pos[u],pos[u]+size[u]-1,pos[v]+size[v],c[i],-1};
        }else{
            int t=v;
            for(int k=B;k>=0;--k)
                if(dep[anc[t][k]]>=dep[u]+1)t=anc[t][k];
            d[++m]=(dish){1,pos[t]-1,pos[v],c[i],1};
            d[++m]=(dish){1,pos[t]-1,pos[v]+size[v],c[i],-1};
            d[++m]=(dish){pos[v],pos[v]+size[v]-1,pos[t]+size[t],c[i],1};
            d[++m]=(dish){pos[v],pos[v]+size[v]-1,n+1,c[i],-1};
        }
    }
    for(int i=1;i<=q;++i){
        int u=read(),v=read(),k=read();
        if(pos[u]>pos[v])swap(u,v);
        f[i]=(fruit){pos[u],pos[v],k,i};
    }
    sort(d+1,d+m+1,cmp1);
    sort(f+1,f+q+1,cmp2);
    sort(c+1,c+p+1);
    int len=unique(c+1,c+p+1)-c-1;
    solve(1,q,1,m,1,len);
    for(int i=1;i<=q;++i)printf("%d\n",ans[i]);
    return 0;
}

+++++++++++++++++++++++++++++++++++++++++++

+本文作者:luyouqi233。               +

+欢迎访问我的博客:http://www.cnblogs.com/luyouqi233/+

+++++++++++++++++++++++++++++++++++++++++++

原文地址:https://www.cnblogs.com/luyouqi233/p/8969606.html

时间: 2024-11-13 00:57:13

BZOJ4009 & 洛谷3242 & LOJ2113:[HNOI2015]接水果——题解的相关文章

洛谷3768:简单的数学题——题解

https://www.luogu.org/problemnew/show/P3768 题面来自洛谷,因为没用markdown所以直接截的图. 剩余的图是我用markdown写完然后截的图. 参考洛谷第一篇题解. 这个式子直观感受就需要莫比乌斯反演,大致的过程参考:BZOJ2693:jzptab 那么跳过暴力推式子,我们能够得到: (如果你疑问为什么是miu(k/d)而不是miu(d),其实二者皆可,但是为什么这么干请往下看) 显然可以分块O(sqrt(n))做,那么后面的那一串东西怎么做呢?

洛谷3919:可持久化数组——题解

https://www.luogu.org/problemnew/show/P3919 如题,你需要维护这样的一个长度为 N 的数组,支持如下几种操作 在某个历史版本上修改某一个位置上的值 访问某个历史版本上的某一位置的值 此外,每进行一次操作(对于操作2,即为生成一个完全一样的版本,不作任何改动),就会生成一个新的版本.版本编号即为当前操作的编号(从1开始编号,版本0表示初始状态数组) 这题题意看错了就很伤……操作2新建的版本是它所询问的历史版本emmm…… 以及各种小错误,int没retur

洛谷 P1005 矩阵取数游戏 题解

此文为博主原创题解,转载时请通知博主,并把原文链接放在正文醒目位置. 题目链接:https://www.luogu.org/problem/show?pid=1005 题目描述 帅帅经常跟同学玩一个矩阵取数游戏:对于一个给定的n*m的矩阵,矩阵中的每个元素aij均为非负整数.游戏规则如下: 1.每次取数时须从每行各取走一个元素,共n个.m次后取完矩阵所有元素: 2.每次取走的各个元素只能是该元素所在行的行首或行尾: 3.每次取数都有一个得分值,为每行取数的得分之和,每行取数的得分 = 被取走的元

洛谷 P1507 NASA的食物计划 题解

此文为博主原创题解,转载时请通知博主,并把原文链接放在正文醒目位置. 题目链接:https://www.luogu.org/problem/show?pid=1507 题目背景 NASA(美国航空航天局)因为航天飞机的隔热瓦等其他安 全技术问题一直大伤脑筋,因此在各方压力下终止了航天 飞机的历史,但是此类事情会不会在以后发生,谁也无法 保证,在遇到这类航天问题时,解决方法也许只能让航天 员出仓维修,但是多次的维修会消耗航天员大量的能量, 因此NASA便想设计一种食品方案,让体积和承重有限的 条件

BZOJ3343 &amp; 洛谷2801:教主的魔法——题解

http://www.lydsy.com/JudgeOnline/problem.php?id=3343 https://www.luogu.org/problemnew/show/2801 题目描述 教主最近学会了一种神奇的魔法,能够使人长高.于是他准备演示给XMYZ信息组每个英雄看.于是N个英雄们又一次聚集在了一起,这次他们排成了一列,被编号为1.2.…….N. 每个人的身高一开始都是不超过1000的正整数.教主的魔法每次可以把闭区间[L, R](1≤L≤R≤N)内的英雄的身高全部加上一个整

洛谷P1047校门外的树题解

摘要: 此题是一个模拟题,但需要注意的一点就是它的树是从数轴的0开始,所以我们也要从0开始,这样才能实现代码. 代码: #include<iostream> using namespace std; int s[100000]; int main() { int l,m,x,y,d=0; cin>>l>>m; for(int i=0; i<=l; i++) s[i]=1; for(int i=0; i<m; i++) { cin>>x>&g

【p1897】洛谷P1897电梯里的爱情题解

这个题是一个搜索题,可以先算出最高楼层,并算出不重复的楼层的个数,要注意的一点就是一定不要把0楼算在内. 代码 #include<iostream> #include<cstring> #include<bits/stdc++.h> using namespace std; int n,a[1000010],b[1000010],ans=0,maxn=-1,sum=0; int main() { cin>>n; for(int i=0; i<n; i+

洛谷 P1101 【单词方阵】题解

来先写一下思路: 1.一一枚举开始的位置 2.朝8个方向搜索(其实不如说是递归) 3.在搜索到后标记搜索到了 4.通过标记在搜索完成后再标记哪些地方是"yizhong" 5.输出 严格来说,此题不算是深搜,到不如说是递归,因为只需要往前探路,不需要回溯,下面是代码: #include<iostream>#include<cstdio>using namespace std;int pd,wx[9] = {0,-1,-1,0,1,1,1,0,-1},wy[9] =

洛谷 P1184高手之在一起 题解

题目传送门 那位高手是谁啊?@jxpxcsh  QWQ. 这道题数据特别水,所以直接使用O(n*m),每读进一个m内的字符串,就扫一遍n的字符串.但注意地点字符串中有可能会有空格,所以这时候就要请出getline出场了. #include<bits/stdc++.h> using namespace std; string gs[21],ll; int n,m,ans=0; int main(){ cin>>n>>m; getline(cin,ll); for(int