zoukankan      html  css  js  c++  java
  • POJ 2886 Who Gets the Most Candies? (线段树)题解

    题意:一堆小朋友围成一个圈,规定从k开始玩,每个被选中的人都有一个数字,正数代表从他左边开始数num,负数从右边数,被选中的人继续按照上述操作,直到都退出圈子,第i个退圈的人能拿到一个点数,这个点数是i的因数个数(比如第4个人拿3点)。问:谁点数最多,一样多选第一个。

    思路:一道好题啊,万万没想到是线段树,想要想到感觉还是有点难度的。这道题其实就是要一直维护剩余人数,但是以前没做过线段树维护剩余人数的,一时半会想不到...首先,我们要用打表找到所有点数。然后用线段树维护剩余人数,这里有一个剩余人数位置和所有人数位置转换的过程,是用线段树解决的。线段树的每个节点储存的是某个区间的剩余人数,所以我们在用一个人在剩余人中的位置找到他的编号时,是比较sum节点而非L,R。还有向左向右方向要注意,画个圈慢慢感受。具体看注释。

    代码:

    #include<cstdio>
    #include<vector>
    #include<stack>
    #include<queue>
    #include<cstring>
    #include<string>
    #include<cmath>
    #include<cstdlib>
    #include<algorithm>
    #define ll long long
    const int maxn = 500000+5;
    const int MOD = 1e7;
    const int INF = 0x3f3f3f3f;
    using namespace std;
    struct node{
        char name[12];
        int num;
    }e[maxn];
    int get[maxn],sum[maxn << 2];
    void pushup(int rt){
        sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];
    }
    void build(int l,int  r,int rt){
        if(l == r){
            sum[rt] = 1;
            return;
        }
        int m = (l + r) >> 1;
        build(l,m,rt << 1);
        build(m + 1,r,rt << 1 | 1);
        pushup(rt);
    }
    int update(int pos,int l,int r,int rt){
        if(l == r){
            sum[rt] = 0;
            return l;
        }
        int ans;
        int m = (l + r) >> 1;
        if(pos <= sum[rt << 1]) //pos代表在剩余人数中的第几个,所以写法略有不同
            ans = update(pos,l,m,rt << 1);
        else
            ans = update(pos - sum[rt << 1],m + 1,r,rt << 1 | 1);
        pushup(rt);
        return ans;
    }
    void init(){
        int top = 500000;
        memset(get,0,sizeof(get));
        for(int i = 1;i <= top;i++){
            get[i]++;
            for(int j = 2*i;j <= top;j += i){
                get[j]++;
            }
        }
    }
    int main(){
        init();
        int n,k;
        while(~scanf("%d%d",&n,&k)){
            build(1,n,1);
            for(int i = 1;i <= n;i++)
                scanf("%s%d",e[i].name,&e[i].num);
            int aim = 0,MAX = -1;
            for(int i = 1;i <= n;i++){
                if(get[i] > MAX){
                    aim = i;
                    MAX = get[i];
                }
            }
            int pos;    //开始计算的位置
            int mov,all,now = 0;
            while(true){
                pos = update(k,1,n,1);    //被宰的是哪个人
                now++;
                if(now == aim) break;
                mov = e[pos].num;
                if(mov > 0){    //在左边
                    k = (k - 1 + mov - 1)%sum[1] + 1;   //这里的pos是剩余人中的编号
                    //第一个-1是因为pos这个人已经被宰了,所以他这个位置其实是没有的
                    //第二个-1是防止%之后归0,所以先-1再+1
                }
                else{   //在右边
                    k = ((k - 1 + mov)%sum[1] + sum[1])%sum[1] + 1;
                }
            }
            printf("%s %d
    ",e[pos].name,MAX);
        }
        return 0;
    }
    
  • 相关阅读:
    复合索引的列顺序判断
    sqlserver 存储过程中使用临时表到底会不会导致重编译
    Sql Server参数化查询之where in和like实现详解
    浅析SqlServer简单参数化模式下对sql语句自动参数化处理以及执行计划重用
    SQL优化 查询语句中,用 inner join 作为过滤条件和用where作为过滤条件的区别
    通过手动创建统计信息优化sql查询性能案例
    SQL Server 并行操作优化,避免并行操作被抑制而影响SQL的执行效率
    SQL Server 执行计划利用统计信息对数据行的预估原理二(为什么复合索引列顺序会影响到执行计划对数据行的预估)
    C#实现文件数据库
    ASP.NET DropDownList1_SelectedIndexChanged使用
  • 原文地址:https://www.cnblogs.com/KirinSB/p/9408753.html
Copyright © 2011-2022 走看看