zoukankan      html  css  js  c++  java
  • [SDOI2013]森林

    Description:

    小Z有一片森林,含有N个节点,每个节点上都有一个非负整数作为权值。初始的时候,森林中有M条边。

    小Z希望执行T个操作,操作有两类:

    (1.Q x y k)查询点(x)到点(y)路径上所有的权值中,第k小的权值是多少。此操作保证点x和点y连通,同时这两个节点的路径上至少有k个点。
    (2.L x y)在点(x)和点(y)之间连接一条边。保证完成此操作后,仍然是一片森林

    Hint:

    $n,m,T<=8*10^{4} $

    Solution:

    可以发现询问就是(Count On A Tree)的询问,现在问题是如何动态维护森林
    联想到启发式合并,每次暴力在较小的树上重建主席树和倍增数组,一个(log)还是可以承受的

    #include<bits/stdc++.h>
    using namespace std;
    const int mxn=8e4+5,mxm=2e7+5;
    struct ed {
        int to,nxt;
    }t[mxn<<1];
    int n,m,q,s,ans,tot,cnt;
    int a[mxn],b[mxn],dep[mxn],anc[mxn],sum[mxm],vis[mxn],hd[mxn],sz[mxn],ls[mxm],rs[mxm],rt[mxm],f[mxn][19];
    
    inline void add(int u,int v) {
        t[++cnt]=(ed){v,hd[u]},hd[u]=cnt;
    }
    
    int find(int x) {
        return anc[x]==x?x:anc[x]=find(anc[x]);
    }
    
    void update(int,int&,int,int,int);
    
    void dfs(int u,int fa)
    {
        f[u][0]=fa; dep[u]=dep[fa]+1; vis[u]=1;
        update(rt[fa],rt[u],1,s,b[u]);
        for(int i=1;i<=18;++i) f[u][i]=f[f[u][i-1]][i-1];
        for(int i=hd[u];i;i=t[i].nxt) {
            int v=t[i].to;
            if(v==fa) continue ;
            dfs(v,u);
        }
    }
    
    void update(int las,int& p,int l,int r,int val)
    {
        p=++tot; sum[p]=sum[las]+1;
        if(l==r) return ; int mid=(l+r)>>1;
        if(val<=mid) update(ls[las],ls[p],l,mid,val),rs[p]=rs[las];
        else update(rs[las],rs[p],mid+1,r,val),ls[p]=ls[las];
    }
    
    int query(int las1,int las2,int p1,int p2,int l,int r,int k)
    {
        if(l==r) return a[l]; int mid=(l+r)>>1;
        int tp=sum[ls[p1]]+sum[ls[p2]]-sum[ls[las1]]-sum[ls[las2]];
        if(k<=tp) return query(ls[las1],ls[las2],ls[p1],ls[p2],l,mid,k);
        else return query(rs[las1],rs[las2],rs[p1],rs[p2],mid+1,r,k-tp);
    }
    
    void build(int &p,int l,int r,int val)
    {
        p=++tot; sum[p]=1;
        if(l==r) return ; int mid=(l+r)>>1;
        if(val<=mid) build(ls[p],l,mid,val);
        else build(rs[p],mid+1,r,val);
    }
    
    int LCA(int x,int y)
    {
        if(dep[x]<dep[y]) swap(x,y);
        for(int i=18;i>=0;--i) 
            if(dep[f[x][i]]>=dep[y])
                x=f[x][i];
        if(x==y) return x;
        for(int i=18;i>=0;--i)
            if(f[x][i]!=f[y][i])
                x=f[x][i],y=f[y][i];
        return f[x][0];
    }
    
    void link(int& u,int& v)
    {
        u^=ans,v^=ans;
        add(u,v),add(v,u);
        int x=find(u),y=find(v);
        if(sz[x]>sz[y]) swap(x,y),swap(u,v);
        anc[x]=y; sz[y]+=sz[x]; 
    }
    
    void solve(int u,int v,int k)
    {
        u^=ans,v^=ans,k^=ans;
        int lca=LCA(u,v);
        ans=query(rt[lca],rt[f[lca][0]],rt[u],rt[v],1,s,k);
        printf("%d
    ",ans);
    }
    
    int main()
    {
        int u,v,w; char opt[4];
        scanf("%d%d%d%d",&u,&n,&m,&q);
        for(int i=1;i<=n;++i) sz[i]=1,anc[i]=i;
        for(int i=1;i<=n;++i) scanf("%d",&a[i]),b[i]=a[i];
        sort(a+1,a+n+1); s=unique(a+1,a+n+1)-a-1;
        for(int i=1;i<=n;++i) 
            b[i]=lower_bound(a+1,a+n+1,b[i])-a,build(rt[i],1,s,b[i]);
        for(int i=1;i<=m;++i) {
            scanf("%d%d",&u,&v);
            link(u,v); //这里有个细节,如果一开始就
        }
        for(int i=1;i<=n;++i) if(!vis[find(i)]) dfs(find(i),0);
        
        for(int i=1;i<=q;++i) {
            scanf("%s",opt);
            if(opt[0]=='Q') 
                scanf("%d%d%d",&u,&v,&w),solve(u,v,w);
            else 
                scanf("%d%d",&u,&v),link(u,v),dfs(u,v);
        }
        
        return 0;
    }
    
    
  • 相关阅读:
    MKMapVIew学习系列2 在地图上绘制出你运行的轨迹
    WPF SDK研究 Intro(6) WordGame1
    WPF SDK研究 Intro(3) QuickStart3
    WPF SDK研究 Layout(1) Grid
    WPF SDK研究 目录 前言
    WPF SDK研究 Intro(7) WordGame2
    WPF SDK研究 Layout(2) GridComplex
    对vs2005创建的WPF模板分析
    WPF SDK研究 Intro(4) QuickStart4
    《Programming WPF》翻译 第6章 资源
  • 原文地址:https://www.cnblogs.com/list1/p/10374805.html
Copyright © 2011-2022 走看看