BZOJ2588 Count on a tree

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2588

知识点:  可持久化线段树

解题思路:

  先建一棵空的权值线段树,然后按照题目给出的树以任意一点为根的\(DFS\)序来更新这棵线段树。询问\((u,v,k)\)时,其实就是查询\(T[u]\)所对应的线段树加上\(T[v]\)所对应的线段树减去\(T[u and v's LCA]\)所对应的线段树再减去\(T[fa[u and v's LCA]]\),\(LCA\)就是最近公共祖先。(从题解那里学到了一种类似二分的\(query\)的方法)

AC代码:

  1 #include <bits/stdc++.h>
  2 using namespace std;
  3 const int maxn = 100000+5;
  4 int inp[maxn];  //原始输入
  5 int num[maxn],san[maxn];    //san[]:离散化; num[]:下标是DFS序(从1开始),表示这个数在san[]中是第几个
  6 int pos[maxn];      //num[]的下标->inp[]的下标
  7 int rpos[maxn];     //inp[]的下标->num[]的下标
  8 int tot=0,xu=1;
  9 int sum[maxn*20];
 10 int T[maxn*20],lson[maxn*20],rson[maxn*20];
 11 vector<int> G[maxn];
 12 int par[20][maxn],depth[maxn];      //LCA
 13 
 14 void build(int l,int r,int &rt){
 15     rt=++tot;
 16     sum[rt]=0;
 17     if(l==r)    return;
 18     int m=(l+r)>>1;
 19     build(l,m,lson[rt]);    build(m+1,r,rson[rt]);
 20 }
 21 void update(int last,int pos,int l,int r,int &rt){
 22     rt=++tot;
 23     sum[rt]=sum[last]+1;
 24     if(l==r)    return;
 25     lson[rt]=lson[last];
 26     rson[rt]=rson[last];
 27     int m=(l+r)>>1;
 28     if(pos<=m)  update(lson[last],pos,l,m,lson[rt]);
 29     else        update(rson[last],pos,m+1,r,rson[rt]);
 30 }
 31 int lca(int u,int v){
 32     if(depth[u]>depth[v])   swap(u,v);
 33     for(int k=0;k<20;k++){
 34         if((depth[v]-depth[u])>>k&1){
 35             v=par[k][v];
 36         }
 37     }
 38     if(u==v)    return u;
 39     for(int k=19;k>=0;k--){
 40         if(par[k][u]!=par[k][v]){
 41             u=par[k][u];
 42             v=par[k][v];
 43         }
 44     }
 45     return par[0][u];
 46 }
 47 void dfs(int v,int p,int d,int cnt){
 48     num[xu]=lower_bound(san + 1, san + cnt + 1, inp[v]) - san;
 49     pos[xu]=v,rpos[v]=xu;
 50     xu++;
 51 
 52     par[0][v]=p;
 53     depth[v]=d;
 54     for(int i=0;i<G[v].size();i++){
 55         if(G[v][i]!=p)  dfs(G[v][i],v,d+1,cnt);
 56     }
 57 }
 58 void init(int N,int cnt){
 59     dfs(1,0,1,cnt);
 60     for(int k=0;k+1<20;k++){
 61         for(int v=1;v<=N;v++){
 62             if(par[k][v]<=0) par[k+1][v]=0;
 63             else    par[k+1][v]=par[k][par[k][v]];
 64         }
 65     }
 66 }
 67 int que(int x,int y,int rk,int cnt){
 68     int a=x,b=y,c=lca(x,y),d=par[0][c];
 69     a=T[rpos[a]],b=T[rpos[b]],c=T[rpos[c]],d=T[rpos[d]];
 70     int l=1,r=cnt;
 71     int ret=1;
 72     while(l<r){
 73         int m=(l+r)>>1;
 74         int tmp=sum[lson[a]]+sum[lson[b]]-sum[lson[c]]-sum[lson[d]];
 75         if(tmp>=rk) r=m,a=lson[a],b=lson[b],c=lson[c],d=lson[d];
 76         else    rk-=tmp,l=m+1,a=rson[a],b=rson[b],c=rson[c],d=rson[d];
 77     }
 78     return san[l];
 79 }
 80 int main(){
 81 //    freopen("in.txt","r",stdin);
 82     int N,M;
 83     int u,v,k;
 84     scanf("%d%d",&N,&M);
 85     for(int i=1;i<=N;i++){
 86         scanf("%d",&inp[i]);
 87         san[i]=inp[i];
 88     }
 89     sort(san+1,san+1+N);
 90     int cnt=unique(san+1,san+1+N)-san-1;
 91     build(1,cnt,T[0]);  //先建一棵空树
 92     for(int i=1;i<N;i++){
 93         scanf("%d%d",&u,&v);
 94         G[u].push_back(v);
 95         G[v].push_back(u);
 96     }
 97     init(N,cnt);
 98     for(int i=1;i<=N;i++){
 99         int now=pos[i];
100         int fa=par[0][now];
101         update(T[rpos[fa]],num[i],1,cnt,T[i]);  //旧版本是该点的父亲所对应的那个版本
102     }
103     int last=0;
104     while(M--){
105         scanf("%d%d%d",&u,&v,&k);
106         u^=last;
107         last=que(u,v,k,cnt);
108         printf("%d",last);
109         if(M)   printf("\n");
110     }
111 }

 

转载于:https://www.cnblogs.com/Blogggggg/p/8366706.html

  • 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、付费专栏及课程。

余额充值