[bzoj3545]Peaks [bzoj3551]Peaks加强版

8 篇文章 0 订阅
6 篇文章 0 订阅

3545: [ONTAK2010]Peaks

Time Limit: 10 Sec Memory Limit: 128 MB
Submit: 1082 Solved: 289
[Submit][Status][Discuss]
Description

在Bytemountains有N座山峰,每座山峰有他的高度h_i。有些山峰之间有双向道路相连,共M条路径,每条路径有一个困难值,这个值越大表示越难走,现在有Q组询问,每组询问询问从点v开始只经过困难值小于等于x的路径所能到达的山峰中第k高的山峰,如果无解输出-1。

Input

第一行三个数N,M,Q。
第二行N个数,第i个数为h_i
接下来M行,每行3个数a b c,表示从a到b有一条困难值为c的双向路径。
接下来Q行,每行三个数v x k,表示一组询问。

Output

对于每组询问,输出一个整数表示答案。

Sample Input

10 11 4

1 2 3 4 5 6 7 8 9 10

1 4 4

2 5 3

9 8 2

7 8 10

7 1 4

6 7 1

6 4 8

2 1 5

10 8 10

3 4 7

3 4 6

1 5 2

1 5 6

1 5 8

8 9 2

Sample Output

6

1

-1

8

HINT

【数据范围】

N<=10^5, M,Q<=5*10^5,h_i,c,x<=10^9。

我们可以离线处理,对于边的权值大小和询问的权值大小进行排序。这样处理到一个权值为x的询问时,把边加到权值<=x的。
这样用平衡树维护每一个联通块,当两个联通块合并起来的时候,平衡树直接启发式合并一下就行了。
时间复杂度时O(nlog^2n)的。

3551: [ONTAK2010]Peaks加强版

加强版要求强制在线。我们其实可以发现,有用的边只是这个图的最小生成树上面的。
所以,我们求一遍最小生成树,对于其中的一条边,我们新建一个节点,权值为这条边的权值,然后分别连向这两个点,删去原来的边。
这样我们就建出了一棵树,这棵树的叶子节点都是原图中的节点,而且满足大根堆得性质。
这样对于一个询问x,v,k只需要从x向上倍增到最浅的比v小的祖先,这个祖先的子树中的叶子节点就是点x所能到的所有的原图上面的点。
所以我们只需要先预处理出来dfs序,对于dfs序建一个主席树,然后每次询问直接查区间第k大就行了。

3545code:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=100010;
const int M=1000010;
int t,n,m,p,v[N],fa[N];
struct S{int st,en,va;}l[M];
struct Q{int x,v,k,No,ans;}q[M];
inline int in(){
    int x=0;char ch=getchar();
    while(ch<'0'||ch>'9') ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x;
}
inline bool cmp_1(S x,S y){return x.va<y.va;}
inline bool cmp_2(Q x,Q y){return x.v<y.v;}
inline bool cmp_3(Q x,Q y){return x.No<y.No;}
inline int find(int x){
    if(x!=fa[x]) fa[x]=find(fa[x]);
    return fa[x];
}
struct Node{
    Node*ch[2];
    int num,r,v,s;
    Node(int v):v(v){num=1;ch[0]=ch[1]=NULL;r=rand();}
    int cmp(int x) const{
        if(x==v) return -1;
        else return (x<v?1:0);
    }
    void Maintain(){
        s=num;
        if(ch[0]!=NULL) s+=ch[0]->s;
        if(ch[1]!=NULL) s+=ch[1]->s;
    }
};
Node *root[N];
void rotate(Node* &o,int d){
    Node* k=o->ch[d^1]; o->ch[d^1]=k->ch[d]; k->ch[d]=o;
    o->Maintain(); k->Maintain(); o=k;
}
void insert(Node* &o,int x){
    if(o==NULL) o=new Node(x);
    else {
        int d=o->cmp(x);
        if(d==-1) o->num+=1;
        else{
            insert(o->ch[d],x);
            if(o->r < o->ch[d]->r) rotate(o,d^1);
        }
    }
    o->Maintain();
}
void del(Node* &o,int x){
    int d=o->cmp(x);
    if(d==-1){
        if(o->num==1){
            if(o->ch[0]==NULL) o=o->ch[1];
            else if(o->ch[1]==NULL) o=o->ch[0];
            else{
                int d2=(o->ch[0]->r > o->ch[1]->r?1:0);
                rotate(o,d2); del(o->ch[d2],x);
            }
        }
        else o->num-=1;
    }
    else del(o->ch[d],x);
    if(o!=NULL) o->Maintain();
}
int kth(Node* &o,int x){
    if(x<=0||x>o->s||o==NULL) return 0;
    int d=(o->ch[0]==NULL?0:o->ch[0]->s);
    if(d+1<=x&&x<=d+o->num) return o->v;
    if(x<=d) return kth(o->ch[0],x);
    else return kth(o->ch[1],x-d-o->num);
}
int main(){
    int i,j,now=1,x,y;
    t=in();n=in();m=in();p=in();
    for(i=1;i<=n;++i) v[i]=in();
    for(i=1;i<=m;++i) l[i].st=in(),l[i].en=in(),l[i].va=in();
    sort(l+1,l+m+1,cmp_1);
    for(i=1;i<=p;++i) q[i].x=in(),q[i].v=in(),q[i].k=in(),q[i].No=i;
    sort(q+1,q+p+1,cmp_2);
    for(i=1;i<=n;++i) fa[i]=i,insert(root[i],v[i]);
    for(i=1;i<=p;++i){
        for(;l[now].va<=q[i].v&&now<=m;++now){
            x=l[now].st,y=l[now].en;
            int r1=find(x),r2=find(y);
            if(r1==r2) continue;
            if(root[r1]->s < root[r2]->s) swap(r1,r2);
            fa[r2]=r1;
            int xx=root[r2]->s;
            while(xx){
                int yy=kth(root[r2],xx);
                insert(root[r1],yy);
                xx-=1;
            }
        }
        x=find(q[i].x);
        if(root[x]->s >= q[i].k) q[i].ans=kth(root[x],q[i].k);
        else q[i].ans=-1;
    }
    sort(q+1,q+p+1,cmp_3);
    for(i=1;i<=p;++i) printf("%d\n",q[i].ans);
}

3551code:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define inf 0x7fffffff
const int N=200010;
const int M=500010;
const int O=5000000;
struct S{int st,en,va;}aa[N<<1],p[M];
int root[O],l[O],r[O],sum[O],point[N],next[N<<1],use[N],to[N],ans;
int n,m,q,v[N],Fa[N],Root,h[N],minn[N],maxn[N],dfsn,fa[N][20],deep[N],map[N],tot;
inline int in(){
    int x=0;char ch=getchar();
    while(ch<'0'||ch>'9') ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x;
}
inline bool cmp(S x,S y){return x.va<y.va;}
inline int find(int x){
    if(x!=Fa[x]) Fa[x]=find(Fa[x]);
    return Fa[x];
}
inline void add(int x,int y){
    tot+=1;next[tot]=point[x];point[x]=tot;
    aa[tot].st=x;aa[tot].en=y;
    tot+=1;next[tot]=point[y];point[y]=tot;
    aa[tot].st=y;aa[tot].en=x;
}
inline void dfs(int x,int last){
    int i;
    for(i=1;i<=19&&deep[x]>=(1<<i);++i)
      fa[x][i]=fa[fa[x][i-1]][i-1];
    if(x<=n) minn[x]=maxn[x]=++dfsn,map[dfsn]=x;
    else minn[x]=inf,maxn[x]=0;
    for(i=point[x];i;i=next[i])
      if(aa[i].en!=last){
        fa[aa[i].en][0]=x;
        deep[aa[i].en]=deep[x]+1;
        dfs(aa[i].en,x);
        minn[x]=min(minn[x],minn[aa[i].en]);
        maxn[x]=max(maxn[x],maxn[aa[i].en]);
      }
}
#define mid (L+R)/2
inline void insert(int L,int R,int x,int &y,int z){
    y=++tot;
    sum[y]=sum[x]+1;
    if(L>=R) return ;
    l[y]=l[x];r[y]=r[x];
    if(z<=mid) insert(L,mid,l[x],l[y],z);
    else insert(mid+1,R,r[x],r[y],z);
}
inline void query(int L,int R,int x,int y,int z){
    int now=sum[l[y]]-sum[l[x]];
    if(L==R){
        ans=to[L];
        printf("%d\n",ans);
        return ;
    }
    if(now<z) query(mid+1,R,r[x],r[y],z-now);
    else query(L,mid,l[x],l[y],z);
}
int main(){
    int i,j,x,y,z,num=0,lca,size;
    n=in();m=in();q=in();Root=n;
    for(i=1;i<=n;++i) h[i]=in(),use[i]=h[i];
    sort(use+1,use+n+1);
    size=unique(use+1,use+n+1)-use-1;
    for(i=1;i<=n;++i)
      x=h[i],h[i]=upper_bound(use+1,use+size+1,h[i])-use-1,to[h[i]]=x;
    for(i=1;i<=m;++i) p[i].st=in(),p[i].en=in(),p[i].va=in();
    sort(p+1,p+m+1,cmp);
    for(i=1;i<=n*2;++i) Fa[i]=i;
    for(i=1;i<=m;++i){
        x=p[i].st,y=p[i].en;
        int r1=find(x),r2=find(y);
        if(r1!=r2){
            ++num;
            add(++Root,r1);add(Root,r2);
            Fa[r1]=Fa[r2]=Root;v[Root]=p[i].va;
        }
        if(num==n-1) break;
    }
    dfs(Root,0);
    for(tot=0,i=1;i<=n;++i) insert(1,size,root[i-1],root[i],h[map[i]]);
    while(q--){
        x=in();y=in();z=in();
        if(ans!=-1) x^=ans,y^=ans,z^=ans;
        for(i=19;~i;--i)
          if(deep[x]>=(1<<i)&&v[fa[x][i]]<=y) x=fa[x][i];
        if(maxn[x]-minn[x]+1<z){printf("-1\n");ans=-1;continue;}
        query(1,size,root[minn[x]-1],root[maxn[x]],maxn[x]-minn[x]+2-z);
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值