zoukankan      html  css  js  c++  java
  • poj2886(线段树求序列第k小)

    题目链接:https://vjudge.net/problem/POJ-2886

    题意:n个人围成一个圈,每个人有姓名s和权值val两个属性,第一轮序号为k的人退出,并根据其val指定下一个人,val为正即其右第val个人,val为负及其左第val个人。求第p个出局的人的姓名,其中p的约数最多,并输出约数的数量。

    思路:首先是数学问题,可通过唯一分解定理或筛法打表计算出n个人时第几个出局的人的约数最多f1[n],和约数的数量f2[n],数据很小,不打表的话会超时。然后进行f1[n]次循环,每次找出当前序列中第k小的人。利用线段树来实现,线段树的结点属性sum表示该区间剩余数的个数,每次更新时都要减一。最后就是确定每次的k值。

                  if(tmp>0)
                      k=((k-1+tmp-1)%Mod+Mod)%Mod+1;
                  else
                      k=((k+tmp-1)%Mod+Mod)%Mod+1;
    tmp>0时,因为要将k删除,那么k的下一位仍是第k小,所以要减1,后面的减一再加一是避免出现0。tmp<0时,k的下一位是第k-1小,就不用减1了。自己举个例子模拟一下就懂了。

    AC代码:

    #include<cstdio>
    using namespace std;
    const int maxn=500005;
    
    struct node1{
        char s[12];
        int val;
    }boy[maxn];
    
    struct node2{
        int l,r,sum;
    }tr[maxn<<2];
    
    int f1[40]={0,1,2,4,6,12,24,36,48,60,120,180,240,360,720,
                840,1260,1680,2520,5040,7560,10080,15120,
                20160,25200,27720,45360,50400,55440,83160,
                110880,166320,221760,277200,332640,498960,500001};
    int f2[40]={0,1,2,3,4,6,8,9,10,12,16,18,20,24,30,32,36,40,
                48,60,64,72,80,84,90,96,100,108,120,128,
                144,160,168,180,192,200};
    int n,k;
    
    void build(int v,int l,int r){
        tr[v].l=l,tr[v].r=r,tr[v].sum=r-l+1;
        if(l==r) return;
        int mid=(l+r)>>1;
        build(v<<1,l,mid);
        build(v<<1|1,mid+1,r);
    }
    
    int update(int v,int k){
        --tr[v].sum;
        if(tr[v].l==tr[v].r)
            return tr[v].r;
        if(tr[v<<1].sum>=k) return update(v<<1,k);
        else return update(v<<1|1,k-tr[v<<1].sum);
    }
    
    int main(){
        while(~scanf("%d%d",&n,&k)){
            for(int i=1;i<=n;++i)
                scanf("%s%d",boy[i].s,&boy[i].val);
            int num,nump,nw=0,Mod=n,pos;
            for(int i=1;i<=39;++i)
                if(n>=f1[i]&&n<f1[i+1]){
                    num=f1[i];
                    nump=f2[i];
                    break;
                }
            build(1,1,n);
            while(1){
                --Mod;
                pos=update(1,k);
                if(++nw==num) break;
                int tmp=boy[pos].val;
                if(tmp>0) 
                    k=((k-1+tmp-1)%Mod+Mod)%Mod+1;
                else
                    k=((k+tmp-1)%Mod+Mod)%Mod+1;
            }
            printf("%s %d
    ",boy[pos].s,nump);
        }
        return 0;
    }
  • 相关阅读:
    图像分割学习笔记_1(opencv自带meanshift分割例子)
    基础学习笔记之opencv(10):Mat mask操作
    Qt学习之路_6(Qt局域网聊天软件)
    Qt学习之路_8(Qt中与文件目录相关操作)
    Android开发历程_6(RadioButton和CheckBox的使用)
    Qt学习之路_4(Qt UDP的初步使用)
    目标跟踪学习笔记_4(particle filter初探3)
    Reading papers_11(读Integrating local action elements for action analysis相关文章)
    基础学习笔记之opencv(8):Mat 基本图像容器
    基础学习笔记之opencv(14):随机数发生器&绘制文字
  • 原文地址:https://www.cnblogs.com/FrankChen831X/p/10789816.html
Copyright © 2011-2022 走看看