• COJ980 WZJ的数据结构(负二十)


    试题描述
    在Bytemountains有N座山峰,每座山峰有他的高度h_i。有些山峰之间有双向道路相连,共M条路径,每条路径有一个困难值,这个值越大表示越难走,现在有Q组询问,每组询问询问从点v开始只经过困难值小于等于x的路径所能到达的山峰中第k高的山峰,如果无解输出-1。
    输入
    第一行三个数N,M,Q。
    第二行N个数,第i个数为h_i
    接下来M行,每行3个数a b c,表示从a到b有一条困难值为c的双向路径。
    接下来Q行,每行三个数v x k,表示一组询问。
    输出
    对于每组询问,输出一个整数表示答案。
    输入示例
    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
    输出示例
    6
    1
    -1
    8
    其他说明
    N<=100000, M,Q<=500000,h_i,c,x<=10^9

    在线算法:

    做一次最小生成树,考虑在连边x,y时新建节点ToT,连接ToT->findset(x),ToT->findset(y),将ToT的权值赋为边(x,y)的权值

    这样有什么好处呢?对于x走不超过v的边能到达的节点就是x向上倍增最上面一个rt的子树的叶结点,DFS序+主席树就可以做k大了。

    这是正常版的O(nlogn)(2481ms)

    #include<cstdio>
    #include<cctype>
    #include<queue>
    #include<cstring>
    #include<algorithm>
    #define rep(s,t) for(int i=s;i<=t;i++)
    #define ren for(int i=first[x];i;i=next[i])
    using namespace std;
    inline int read() {
        int x=0,f=1;char c=getchar();
        for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
        for(;isdigit(c);c=getchar()) x=x*10+c-'0';
        return x*f;
    }
    inline void print(int x) {
        if(x==0){putchar('0');return;}if(x<0) putchar('-'),x=-x;
        int len=0,buf[15];while(x) buf[len++]=x%10,x/=10;
        for(int i=len-1;i>=0;i--) putchar(buf[i]+'0');putchar('
    ');
    }
    const int maxn=200010;
    const int maxm=500010;
    const int maxnode=5000010;
    struct Edge {
        int u,v,w;
        bool operator < (const Edge& ths) const {
            return w<ths.w;
        }
    }e[maxm];
    int n,m,q,ToT,h[maxn],val[maxn],pa[maxn];
    int lastans,tmp[maxn];
    int findset(int x) {return x==pa[x]?pa[x]:pa[x]=findset(pa[x]);}
    int first[maxn],next[maxn],to[maxn],es;
    void AddEdge(int u,int v) {
        to[++es]=v;next[es]=first[u];first[u]=es;
    }
    int fa[maxn][20],Ln[maxn],Rn[maxn],vis[maxn],sz;
    int root[maxn<<1],ls[maxnode],rs[maxnode],s[maxnode],TOT;
    void update(int& y,int x,int l,int r,int pos) {
        s[y=++TOT]=s[x]+1;if(l==r) return;
        int mid=l+r>>1;ls[y]=ls[x];rs[y]=rs[x];
        if(pos<=mid) update(ls[y],ls[x],l,mid,pos);
        else update(rs[y],rs[x],mid+1,r,pos);
    }
    int query(int x,int y,int l,int r,int k) {
        if(l==r) return l;
        int k2=s[rs[y]]-s[rs[x]],mid=l+r>>1;
        if(k2>=k) return query(rs[x],rs[y],mid+1,r,k);
        return query(ls[x],ls[y],l,mid,k-k2);
    }
    void dfs(int x) {
        Ln[x]=++sz;vis[x]=1;
        if(x<=n) update(root[sz],root[sz-1],1,n,lower_bound(tmp+1,tmp+n+1,h[x])-tmp);
        else root[sz]=root[sz-1];
        rep(1,19) fa[x][i]=fa[fa[x][i-1]][i-1];
        ren fa[to[i]][0]=x,dfs(to[i]);
        Rn[x]=++sz;root[sz]=root[sz-1];
    }
    int findrt(int x,int v) {
        for(int i=19;i>=0;i--) if(val[fa[x][i]]<=v) x=fa[x][i];
        return x;
    }
    int main() {
        val[0]=2e9;
        n=ToT=read();m=read();q=read();
        rep(1,n) tmp[i]=h[i]=read(),pa[i]=i;
        sort(tmp+1,tmp+n+1);
        rep(1,m) e[i].u=read(),e[i].v=read(),e[i].w=read();
        sort(e+1,e+m+1);
        rep(1,m) {
            int u=findset(e[i].u),v=findset(e[i].v);
            if(u!=v) {
                val[++ToT]=e[i].w;pa[ToT]=ToT;
                AddEdge(ToT,u);AddEdge(ToT,v);
                pa[u]=pa[v]=ToT;
            }
        }
        rep(1,n) if(!vis[i]) dfs(findset(i));
        rep(1,q) {
            int v=read(),x=read(),k=read();
            int rt=findrt(v,x),r1=root[Ln[rt]-1],r2=root[Rn[rt]];
            if(s[r2]-s[r1]<k) print(lastans=-1);
            else print(lastans=tmp[query(r1,r2,1,n,k)]);
        }
        return 0;
    }
    View Code

    这是自己写人工栈版的(本机第一种写法会爆栈,无法出数据)O(nlogn)(2606ms)

    #include<cstdio>
    #include<cctype>
    #include<queue>
    #include<stack>
    #include<cstring>
    #include<algorithm>
    #define rep(s,t) for(int i=s;i<=t;i++)
    #define ren for(int i=first[x];i;i=next[i])
    using namespace std;
    inline int read() {
        int x=0,f=1;char c=getchar();
        for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
        for(;isdigit(c);c=getchar()) x=x*10+c-'0';
        return x*f;
    }
    const int maxn=200010;
    const int maxm=500010;
    const int maxnode=7000010;
    struct Edge {
        int u,v,w;
        bool operator < (const Edge& ths) const {
            return w<ths.w;
        }
    }e[maxm];
    int n,m,q,ToT,h[maxn],val[maxn],pa[maxn];
    int lastans,tmp[maxn];
    int findset(int x) {return x==pa[x]?pa[x]:pa[x]=findset(pa[x]);}
    int first[maxn],next[maxn],to[maxn],es;
    void AddEdge(int u,int v) {
        to[++es]=v;next[es]=first[u];first[u]=es;
    }
    int fa[maxn][20],Ln[maxn],Rn[maxn],vis[maxn],sz;
    int root[maxn<<1],ls[maxnode],rs[maxnode],s[maxnode],TOT;
    void update(int& y,int x,int l,int r,int pos) {
        s[y=++TOT]=s[x]+1;if(l==r) return;
        int mid=l+r>>1;ls[y]=ls[x];rs[y]=rs[x];
        if(pos<=mid) update(ls[y],ls[x],l,mid,pos);
        else update(rs[y],rs[x],mid+1,r,pos);
    }
    int query(int x,int y,int l,int r,int k) {
        if(l==r) return l;
        int k2=s[rs[y]]-s[rs[x]],mid=l+r>>1;
        if(k2>=k) return query(rs[x],rs[y],mid+1,r,k);
        return query(ls[x],ls[y],l,mid,k-k2);
    }
    stack<int> S;
    void dfs(int x) {
        S.push(x);
        while(!S.empty()) {
            x=S.top();
            if(!vis[x]) {
                vis[x]=1;Ln[x]=++sz;
                if(x<=n) update(root[sz],root[sz-1],1,n,lower_bound(tmp+1,tmp+n+1,h[x])-tmp);
                else root[sz]=root[sz-1];
                rep(1,19) fa[x][i]=fa[fa[x][i-1]][i-1];
                ren fa[to[i]][0]=x,S.push(to[i]);
            }
            else Rn[x]=++sz,root[sz]=root[sz-1],S.pop();
        }
    }
    int findrt(int x,int v) {
        for(int i=19;i>=0;i--) if(val[fa[x][i]]<=v) x=fa[x][i];
        return x;
    }
    int main() {
        val[0]=2e9;
        n=ToT=read();m=read();q=read();
        rep(1,n) tmp[i]=h[i]=read(),pa[i]=i;
        sort(tmp+1,tmp+n+1);
        rep(1,m) e[i].u=read(),e[i].v=read(),e[i].w=read();
        sort(e+1,e+m+1);
        rep(1,m) {
            int u=findset(e[i].u),v=findset(e[i].v);
            if(u!=v) {
                val[++ToT]=e[i].w;pa[ToT]=ToT;
                AddEdge(ToT,u);AddEdge(ToT,v);
                pa[u]=pa[v]=ToT;
            }
        }
        rep(1,n) if(!vis[i]) dfs(findset(i));
        rep(1,q) {
            int v=read(),x=read(),k=read();
            int rt=findrt(v,x),r1=root[Ln[rt]-1],r2=root[Rn[rt]];
            if(s[r2]-s[r1]<k) printf("%d
    ",lastans=-1);
            else printf("%d
    ",lastans=tmp[query(r1,r2,1,n,k)]);
        }
        return 0;
    }
    View Code

    离线算法:

    考虑将操作与边升序排序,离线用平衡树维护每个连通块的答案,连边时启发式合并即可

    写成了int ans[maxn]调哭了233 O(nlog^2n)(1607ms)

    #include<cstdio>
    #include<cctype>
    #include<queue>
    #include<ctime>
    #include<cstring>
    #include<algorithm>
    #define rep(s,t) for(int i=s;i<=t;i++)
    #define ren for(int i=first[x];i!=-1;i=next[i])
    using namespace std;
    inline int read() {
        int x=0,f=1;char c=getchar();
        for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
        for(;isdigit(c);c=getchar()) x=x*10+c-'0';
        return x*f;
    }
    const int maxn=100010;
    const int maxnode=200010;
    const int maxm=500010;
    struct Node {
        Node* ch[2];
        int r,s,v;
        void maintain() {s=ch[0]->s+ch[1]->s+1;}
    }nodes[maxnode],*null=&nodes[0];
    queue<Node*> Q;
    int ToT;
    Node* newnode(int v) {
        Node* o;
        if(!Q.empty()) o=Q.front(),Q.pop();
        else o=&nodes[++ToT];
        o->v=v;o->ch[0]=o->ch[1]=null;o->s=1;o->r=rand();
        return o;
    }
    void del(Node* &o) {Q.push(o);o=null;}
    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 v) {
        if(o==null) o=newnode(v);
        else {
            int d=v>o->v;insert(o->ch[d],v);
            if(o->ch[d]->r>o->r) rotate(o,d^1);
            else o->maintain();
        }
    }
    int query(Node* &o,int k) {
        if(k>o->s) return -1;
        if(k==o->ch[1]->s+1) return o->v;
        if(k<=o->ch[1]->s+1) return query(o->ch[1],k);
        return query(o->ch[0],k-o->ch[1]->s-1);
    }
    void merge(Node* &big,Node* &small) {
        if(small==null) return;
        merge(big,small->ch[0]);merge(big,small->ch[1]);
        insert(big,small->v);del(small);
    }
    void print(Node* &o) {
        if(o==null) return;
        print(o->ch[0]);
        printf("%d ",o->v);
        print(o->ch[1]);
    }
    Node* root[maxn];
    struct Edge {
        int u,v,w,id;
        bool operator < (const Edge& ths) const {
            return w<ths.w;
        }
    }e[maxm],qs[maxm];
    int n,m,q,h[maxn],pa[maxn],ans[maxm];
    int findset(int x) {return x==pa[x]?x:pa[x]=findset(pa[x]);}
    int main() {
        srand(time(0));null->s=0;
        n=read();m=read();q=read();
        rep(1,n) pa[i]=i,root[i]=newnode(h[i]=read());
        rep(1,m) e[i].u=read(),e[i].v=read(),e[i].w=read();
        rep(1,q) qs[i].u=read(),qs[i].w=read(),qs[i].v=read(),qs[i].id=i;
        sort(e+1,e+m+1);sort(qs+1,qs+q+1);int cur=0;
        rep(1,q) {
            while(cur<m&&qs[i].w>=e[cur+1].w) {
                cur++;int u=findset(e[cur].u),v=findset(e[cur].v);
                if(u==v) continue;
                if(root[u]->s>root[v]->s) swap(u,v);
                merge(root[v],root[u]);pa[u]=v;
            }
            ans[qs[i].id]=query(root[findset(qs[i].u)],qs[i].v);
        }
        rep(1,q) printf("%d
    ",ans[i]);
        return 0;
    }
    View Code
  • 相关阅读:
    Luogu4233 射命丸文的笔记 DP、多项式求逆
    LOJ2267 SDOI2017 龙与地下城 FFT、概率密度函数、Simpson
    LOJ2882 JOISC2014 两个人的星座 计算几何
    UOJ343 清华集训2017 避难所 构造、打表
    Solution -「CTS2019」珍珠
    「珂朵莉树」学习笔记
    CSP2019-J/S 游记
    LeetCode(164)Maximum Gap
    LeetCode(165) Compare Version Numbers
    LeetCode(162) Find Peak Element
  • 原文地址:https://www.cnblogs.com/wzj-is-a-juruo/p/4615346.html
Copyright © 2020-2023  润新知