• 2019CCPC网络赛


    hdu6703 array

    题意

    给定一个1到(n)的全排列,两种操作,将(a_{pos})修改为(a_{pos}+1000000),询问第一个大于等于(k)的且不在(a_1...a_r)的数。

    分析

    • 由于(k<=n),因此操作二询问的答案最大是(n+1),因此操作一就相当于删去一个数。
    • 考虑用权值线段树维护下标(权值区间下标最大值),操作一就将下标置为(n+1),而操作二就是在值域([k,n])之间查询下标大于(r)的最小数。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    const int N=1e5+50;
    const int INF=0x3f3f3f3f;
    int T,n,m,a[N],idx[N],o,p,r,k;
    struct VCT{
    #define ls i<<1
    #define rs i<<1|1
    #define mid (l+r)/2
        int mx[N*4];
        void pushup(int i){
            mx[i]=max(mx[ls],mx[rs]);
        }
        void build(int i,int l,int r){
            if(l==r){
                mx[i]=idx[l];
                return;
            }
            build(ls,l,mid);
            build(rs,mid+1,r);
            pushup(i);
        }
        void update(int i,int l,int r,int p){
            if(l==r && l==p){
                mx[i]=n+1;
                return;
            }
            if(p<=mid){
                update(ls,l,mid,p);
            }else{
                update(rs,mid+1,r,p);
            }
            pushup(i);
        }
        //查询值域[ql,qr]中第一个大于k的下标
        int query(int i,int l,int r,int k,int x){
            if(l==r){
                return l;
            }
            int ans=n+1;
            if(k<=mid){
                if(mx[ls]>x){
                    ans=min(ans,query(ls,l,mid,k,x));
                    //因为要找满足条件最小的值,左子树满足直接返回
                    if(ans<n+1){
                        return ans;
                    }
                }
            }
            //这里不能else并列,因为进入左子树查询也有可能查到n+1
            if(mx[rs]<=x){
                return ans;
            }
            return query(rs,mid+1,r,k,x);
        }
    #undef ls
    #undef rs
    #undef mid
    }wa;
    int main(){
    //    freopen("in.txt","r",stdin);
        scanf("%d",&T);
        while(T--){
            scanf("%d%d",&n,&m);
            for(int i=1;i<=n;i++){
                scanf("%d",&a[i]);
                idx[a[i]]=i;
            }
            idx[n+1]=INF;
            n++;
            wa.build(1,1,n);
            int lst=0;
            while(m--){
                scanf("%d",&o);
                if(o==1){
                    scanf("%d",&p);
                    p=p^lst;
                    wa.update(1,1,n,a[p]);
                }else if(o==2){
                    scanf("%d%d",&r,&k);
                    r=r^lst;
                    k=k^lst;
                    lst=wa.query(1,1,n,k,r);
                    printf("%d
    ",lst);
                }
            }
        }
        return 0;
    }
    

    hdu6704 K-th occurence

    题意

    给定一个字符串,多次询问其中一个子串第(k)次出现的位置。

    分析

    • 子串出现位置考虑后缀数组,第(k)小考虑主席树。
    • 对于后缀数组来说,(sa[i])表示后缀出现的位置,那么如果这个后缀对应的(h[i])满足给定子串的长度,那就相当于这个子串出现的位置了。
    • 所以可以二分找到这个子串出现的位置区间,然后主席树维护后缀的出现位置(sa[i]),查询第(k)小即可。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    const int N=1e5+50;
    int T,n,m,l,r,k,s[N];
    char str[N];
    int sa[N],rk[N],h[N];
    int t[N],t2[N],c[N];
    int tr[N];
    int dp[N][25];
    void build_sa(int n,int m=128){
        n++;
        int *x=t,*y=t2;
        for(int i=0;i<m;i++){
            c[i]=0;
        }
        for(int i=0;i<n;i++){
            c[x[i]=s[i]]++;
        }
        for(int i=1;i<m;i++){
            c[i]+=c[i-1];
        }
        for(int i=n-1;i>=0;i--){
            sa[--c[x[i]]]=i;
        }
        for(int k=1;k<=n;k<<=1){
            int p=0;
            for(int i=n-k;i<n;i++){
                y[p++]=i;
            }
            for(int i=0;i<n;i++){
                if(sa[i]>=k){
                    y[p++]=sa[i]-k;
                }
            }
            for(int i=0;i<m;i++){
                c[i]=0;
            }
            for(int i=0;i<n;i++){
                c[x[y[i]]]++;
            }
            for(int i=1;i<m;i++){
                c[i]+=c[i-1];
            }
            for(int i=n-1;i>=0;i--){
                sa[--c[x[y[i]]]]=y[i];
            }
            swap(x,y);
            p=1;
            x[sa[0]]=0;
            for(int i=1;i<n;i++){
                x[sa[i]]=y[sa[i-1]]==y[sa[i]] && y[sa[i-1]+k]==y[sa[i]+k]?p-1:p++;
            }
            if(p>=n){
                break;
            }
            m=p;
        }
        n--;
        for(int i=0;i<=n;i++){
            rk[sa[i]]=i;
        }
        int k=0;
        for(int i=0;i<n;i++){
            if(k){
                k--;
            }
            int j=sa[rk[i]-1];
            while(s[i+k]==s[j+k]){
                k++;
            }
            h[rk[i]]=k;
        }
    }
    void debug(){
        //sa 0~n 包括一个特殊字符
        for(int i=0;i<=n;i++){
            printf("%d ",sa[i]);
        }
        printf("
    ");
        //rk 0~n-1 后缀[i...n-1]的排名
        for(int i=0;i<n;i++){
            printf("%d ",rk[i]);
        }
        printf("
    ");
        //h 1~n 排名为i的后缀与排名为i-1的后缀的LCP
        for(int i=1;i<=n;i++){
            printf("%d ",h[i]);
        }
        printf("
    ");
    }
    struct CT{
    #define mid (l+r)/2
        int tot,sum[N*30],lr[N*30],rr[N*30];
        void init(){
            tot=0;
        }
        int build(int l,int r){
            int rt=++tot;
            sum[rt]=lr[rt]=rr[rt]=0;
            if(l==r){
                return tot;
            }
            lr[rt]=build(l,mid);
            rr[rt]=build(mid+1,r);
            return rt;
        }
        int update(int pre,int l,int r,int v){
            int rt=++tot;
            sum[rt]=sum[pre]+1;
            lr[rt]=lr[pre];
            rr[rt]=rr[pre];
            if(l>=r){
                return rt;
            }
            if(v<=mid){
                lr[rt]=update(lr[pre],l,mid,v);
            }else{
                rr[rt]=update(rr[pre],mid+1,r,v);
            }
            return rt;
        }
        int query(int u,int v,int l,int r,int k){
            if(l>=r){
                return l;
            }
            if(sum[v]-sum[u]<k){
                return -1;
            }
            int lc=sum[lr[v]]-sum[lr[u]];
            if(k<=lc){
                return query(lr[u],lr[v],l,mid,k);
            }else{
                return query(rr[u],rr[v],mid+1,r,k-lc);
            }
        }
    #undef mid
    }ac;
    void init(){
        for(int i=0;i<=n;i++){
            dp[i][0]=h[i];
        }
        for(int j=1;(1<<j)<=n;j++){
            for(int i=0;i+(1<<j)-1<=n;i++){
                dp[i][j]=min(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
            }
        }
    }
    int rmq(int l,int r){
        int k=0;
        while((1<<(k+1))<=r-l+1){
            k++;
        }
        return min(dp[l][k],dp[r-(1<<k)+1][k]);
    }
    int solve(int l,int r){
        if(l==r){
            return n-sa[l];
        }
        if(l>r){
            swap(l,r);
        }
        return rmq(l+1,r);
    }
    int main(){
    //    freopen("in.txt","r",stdin);
        scanf("%d",&T);
        while(T--){
            scanf("%d%d",&n,&m);
            scanf("%s",str);
            for(int i=0;i<n;i++){
                s[i]=str[i]-'a'+1;
            }
            s[n]=0;
            build_sa(n);
    //        debug();
            ac.init();
            tr[0]=ac.build(1,n);
            for(int i=1;i<=n;i++){
                tr[i]=ac.update(tr[i-1],1,n,sa[i]+1);
            }
            init();
            while(m--){
                scanf("%d%d%d",&l,&r,&k);
                l--;
                r--;
                int len=r-l+1;
                int R=rk[l];
                int ql=R,qr=R;
                int ll=1,rr=R;
                while(ll<=rr){
                    int md=(ll+rr)/2;
                    if(solve(md,R)>=len){
                        ql=md;
                        rr=md-1;
                    }else{
                        ll=md+1;
                    }
                }
                ll=R,rr=n;
                while(ll<=rr){
                    int md=(ll+rr)/2;
                    if(solve(R,md)>=len){
                        qr=md;
                        ll=md+1;
                    }else{
                        rr=md-1;
                    }
                }
                if(qr-ql+1<k){
                    printf("-1
    ");
                    continue;
                }
                int ans=ac.query(tr[ql-1],tr[qr],1,n,k);
                printf("%d
    ",ans);
            }
        }
        return 0;
    }
    

    hdu6705 path

    题意

    给一个有向图,求第(k)小的路径。

    分析

    • 这种题似乎都是优先队列来的...
    • 首先把所有点出边排序,最小的放进优先队列里,然后每次取出一条边((u,v)),加入(u)的下一条边和(v)的第一条边(如果有的话)。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N=5e4+50;
    struct Edge{
        int v;
        ll w;
        bool operator <(const Edge &rhs)const{
            return w<rhs.w;
        }
    };
    vector<Edge> g[N];
    int T,n,m,q,k,u,v,w;
    struct node{
        int u,id;
        ll w;
        bool operator <(const node &rhs)const{
            return w>rhs.w;
        }
    };
    int que[N];
    ll ans[N];
    priority_queue<node> pq;
    int main(){
    //    freopen("in.txt","r",stdin);
        scanf("%d",&T);
        while(T--){
            scanf("%d%d%d",&n,&m,&q);
            for(int i=1;i<=n;i++){
                g[i].clear();
            }
            for(int i=1;i<=m;i++){
                scanf("%d%d%d",&u,&v,&w);
                g[u].push_back(Edge{v,1ll*w});
            }
            int mx=0;
            for(int i=1;i<=q;i++){
                scanf("%d",&que[i]);
                mx=max(mx,que[i]);
            }
            while(!pq.empty()){
                pq.pop();
            }
            for(int i=1;i<=n;i++){
                sort(g[i].begin(),g[i].end());
                if(g[i].size()>0){
                    pq.push(node{i,0,g[i][0].w});
                }
            }
            int kk=0;
            //取一条加两条
            while(!pq.empty()){
                node tmp=pq.top();
                pq.pop();
                kk++;
                ans[kk]=tmp.w;
                /**
                 * if(kk==que[i].k){
                 *      i++;
                 *      if(i>q){
                 *          break;
                 *      }
                 * }
                 * 这种写法是错的,如果有多个相同的k,只会计算第一个的答案,后面死循环
                 */
                if(kk==mx){
                    break;
                }
                int u=tmp.u;
                int id=tmp.id;
                int v=g[u][id].v;
                ll w=tmp.w;
                if(g[u].size()>id+1){
                    pq.push(node{u,id+1,w-g[u][id].w+g[u][id+1].w});
                }
                if(g[v].size()>0){
                    pq.push(node{v,0,w+g[v][0].w});
                }
            }
            for(int i=1;i<=q;i++){
                printf("%lld
    ",ans[que[i]]);
            }
        }
        return 0;
    }
    
  • 相关阅读:
    dnu restore 获取失败后的处理
    解决中文乱码问题
    myeclipse10+tomcat6+java8+Struts2.3+win10配置全过程
    Matlab画图plot(X1,Y1,'b -',x1,y1,'ro','MarkerFaceColor','r')
    matlab进行数值近似积分,含变化的常数做为参数
    C#为自定义控件添加事件,以便在使用此控件的窗口进行编辑调用
    C#新添加的控件被旧的遮挡
    C#遍历容器存储顺序
    记一次VMware15.5.1-15018445(版本号)安装与激活,和安装Ubuntu-18.04.4-desktop-amd64(版本号)的过程
    记事本2
  • 原文地址:https://www.cnblogs.com/zxcoder/p/11406957.html
Copyright © 2020-2023  润新知