【BZOJ 3551】Peaks加强版

3 篇文章 0 订阅
2 篇文章 0 订阅

题目描述

在 Bytemountains 有 N N N 座山峰,每座山峰有他的高度 h i h_i hi。有些山峰之间有双向道路相连,共 M M M 条路径,每条路径有一个困难值,这个值越大表示越难走,现在有 Q Q Q 组询问,每组询问询问从点 v v v 开始只经过困难值小于等于 x x x 的路径所能到达的山峰中第 k k k 高的山峰,如果无解输出 − 1 -1 1 N ≤ 1 0 5 N\le 10^5 N105 M , Q ≤ 5 × 1 0 5 M,Q\le 5\times 10^5 M,Q5×105 h i , c , x ≤ 1 0 9 h_i,c,x\le 10^9 hi,c,x109

算法分析

学习了一下 Kruskal 重构树,本题利用了 Kruskal 重构树的堆性质,只需要树上倍增找到能走到的最接近根节点的祖先,所有能到达的山峰就是其所在子树的所有叶子节点,可以用 DFS 序+主席树维护子树中的叶子节点权值。

细节有主席树求区间第 k k k 小而本题要求区间第 k k k 大,需要简单转化一下,还有注意维护的是子树中叶子节点的权值。还有个细节:原来的节点数不一定等于 ⌊ n 2 ⌋ + 1 \lfloor\frac{n}{2}\rfloor+1 2n+1,因为图不一定连通吧。

【BZOJ 3545】Peaks 为不强制在线的版本,双倍经验哦。

代码实现

#include <cstdio>
#include <algorithm>
const int maxn=(int)1e5+5;
const int maxm=(int)5e5+5;
char buf[1<<15],*fs=buf,*ft=buf;
inline char gc() {
    if(fs==ft) {
        ft=(fs=buf)+fread(buf,1,1<<15,stdin);
        if(fs==ft) return 0;
    }
    return *fs++;
}
inline void read(int &num) {
    char c=gc();int f=false;num=0;
    while(c<'0'||'9'<c) {if(c=='-') f=true;c=gc();}
    while('0'<=c&&c<='9') {num=num*10+c-'0';c=gc();}
    if(f) num=-num;
}
int sum[maxn<<6],lch[maxn<<6],rch[maxn<<6],cnt=0;
int ins(int o,int l,int r,int x) {
    int mid=(l+r)>>1,nxt=++cnt;
    if(l==r) sum[nxt]=sum[o]+1;
    else {
        lch[nxt]=(x<=mid)?ins(lch[o],l,mid,x):lch[o];
        rch[nxt]=(mid+1<=x)?ins(rch[o],mid+1,r,x):rch[o];
        sum[nxt]=sum[lch[nxt]]+sum[rch[nxt]];
    }
    return nxt;
}
int query(int x,int y,int l,int r,int k) {
    int mid=(l+r)>>1;if(l==r) return mid;
    int lcnt=sum[lch[y]]-sum[lch[x]];
    if(k<=lcnt) return query(lch[x],lch[y],l,mid,k);
    return query(rch[x],rch[y],mid+1,r,k-lcnt);
}
struct edge {int u,v,w;} e[maxm];
inline bool cmp(const edge &x,const edge &y) {return x.w<y.w;}
int head[maxn<<1],ev[maxn<<1],nxt[maxn<<1],idx=0;
inline void add(int u,int v) {ev[++idx]=v;nxt[idx]=head[u];head[u]=idx;}
int fa[maxn<<1];int find(int x) {return x==fa[x]?x:fa[x]=find(fa[x]);}
int ha[maxn],sz=0;inline int ask(int x) {return std::lower_bound(ha,ha+sz,x)-ha+1;}
int h[maxn<<1],pa[maxn<<1][20],rt[maxn<<1],tot[maxn<<1],dl[maxn<<1],dfn[maxn<<1],dfsidx=0;
void dfs(int x,int fa) {
    dl[dfn[x]=++dfsidx]=x;tot[x]=1;
    for(register int i=head[x];i;i=nxt[i]) {
        int v=ev[i];dfs(v,x);tot[x]+=tot[v];
    }
}
int main() {
    int ln,n,m,q;read(n);ln=n;read(m);read(q);
    for(register int i=1;i<=n;++i) {read(h[i]);ha[i-1]=h[i];}
    std::sort(ha,ha+n);sz=std::unique(ha,ha+n)-ha;
    for(register int i=0;i<m;++i) {read(e[i].u);read(e[i].v);read(e[i].w);}
    std::sort(e,e+m,cmp);
    for(register int i=1;i<=(n<<1);++i) fa[i]=i;
    for(register int i=0;i<m;++i) {
        int x=find(e[i].u),y=find(e[i].v);if(x==y) continue;
        ++n;h[n]=e[i].w;pa[x][0]=pa[y][0]=fa[x]=fa[y]=n;add(n,x);add(n,y);
    }
    for(register int i=1;i<20;++i) for(register int x=1;x<=n;++x) pa[x][i]=pa[pa[x][i-1]][i-1];
    dfs(n,0);for(register int i=1;i<=n;++i) rt[i]=(dl[i]<=ln)?ins(rt[i-1],1,sz,ask(h[dl[i]])):rt[i-1];
    int v,x,k,la=0;
    while(q--) {
        read(v);read(x);read(k);if(~la) {v^=la;x^=la;k^=la;}
        for(int i=19;i>=0;--i) if(pa[v][i]&&h[pa[v][i]]<=x) v=pa[v][i];
        int l=dfn[v],r=dfn[v]+tot[v]-1,cnt=sum[rt[r]]-sum[rt[l-1]];
        printf("%d\n",la=(k<=cnt?ha[query(rt[l-1],rt[r],1,sz,cnt+1-k)-1]:-1));
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
BZOJ 2908 题目是一个数据下载任务。这个任务要求下载指定的数据文件,并统计文件中小于等于给定整数的数字个数。 为了完成这个任务,首先需要选择一个合适的网址来下载文件。我们可以使用一个网络爬虫库,如Python中的Requests库,来帮助我们完成文件下载的操作。 首先,我们需要使用Requests库中的get()方法来访问目标网址,并将目标文件下载到我们的本地计算机中。可以使用以下代码实现文件下载: ```python import requests url = '目标文件的网址' response = requests.get(url) with open('本地保存文件的路径', 'wb') as file: file.write(response.content) ``` 下载完成后,我们可以使用Python内置的open()函数打开已下载的文件,并按行读取文件内容。可以使用以下代码实现文件内容读取: ```python count = 0 with open('本地保存文件的路径', 'r') as file: for line in file: # 在这里实现对每一行数据的判断 # 如果小于等于给定整数,count 加 1 # 否则,不进行任何操作 ``` 在每一行的处理过程中,我们可以使用split()方法将一行数据分割成多个字符串,并使用int()函数将其转换为整数。然后,我们可以将该整数与给定整数进行比较,以判断是否小于等于给定整数。 最后,我们可以将统计结果打印出来,以满足题目的要求。 综上所述,以上是关于解决 BZOJ 2908 数据下载任务的简要步骤和代码实现。 希望对您有所帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值