hdu2886之线段树单点更新

Description

N children are sitting in a circle to play a game.

The children are numbered from 1 to N in clockwise order. Each of them has a card with a non-zero integer on it in his/her hand. The game starts from the K-th child, who tells all the others the integer on his card and jumps out of the circle. The integer on his card tells the next child to jump out. Let A denote the integer. If A is positive, the next child will be the A-th child to the left. If A is negative, the next child will be the (A)-th child to the right.

The game lasts until all children have jumped out of the circle. During the game, the p-th child jumping out will get F(p) candies where F(p) is the number of positive integers that perfectly divide p. Who gets the most candies?

Input

There are several test cases in the input. Each test case starts with two integers  N (0 <  N  ≤ 500,000) and K (1 ≤ K ≤ N) on the first line. The next N lines contains the names of the children (consisting of at most 10 letters) and the integers (non-zero with magnitudes within 108) on their cards in increasing order of the children’s numbers, a name and an integer separated by a single space in a line with no leading or trailing spaces.

Output

Output one line for each test case containing the name of the luckiest child and the number of candies he/she gets. If ties occur, always choose the child who jumps out of the circle first.

Sample Input

4 2
Tom 2
Jack 4
Mary -1
Sam 1

Sample Output

Sam 3

思路和poj2882差不多

建树初始化每个区间的人数sum=right-left+1

然后就是每次对某个位置的人进行更新,更新到的区间人数减1,因为该人退出了

问题在于如何求要查询的人在剩余人中的位置pos

思路:定义x为当前人位置前面的剩余人的个数即1~pos的剩余的人数

可知初始化x=k-1,pos=k,num=n;//num表示剩余的人

对于下一个要查询的人的位置:

--num;

if(s[pos]>0)pos=(x+s[pos])%num;//下一个人的位置是在pos的左边即顺时针数,

else pos=((x+(s[pos]+1))%num+num)%num;//逆时针数,比如s[pos]=-1,则下一个人的位置在pos的右边第一个即第x个位置

if(pos == 0)pos=num;//最后一个位置

x=pos-1;

....

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<queue>

  
  
e std; const int M
#include<algorithm> #include<map> #include<vector> #include<iomanip> #define INF 99999999 using namespa cAX=500000+2; int sum[MAX<<2];//某个区间内剩余的人数 char name[MAX][12]; int s[MAX]; int dp[35][2]={ {1,1},{2,2},{4,3},{6,4},{12,6},{24,8},{36,9},{48,10},{60,12},{120,16},{180,18},{240,20},{360,24},{720,30}, {840,32},{1260,36},{1680,40},{2520,48},{5040,60},{7560,64},{10080,72},{15120,80},{20160,84},{25200,90},{27720,96}, {45360,100},{50400,108},{55440,120},{83160,128},{110880,144},{166320,160},{221760,168},{277200,180},{332640,192},{498960,200} }; /*void Factor(){//计算n内的最多约数编号及个数 int factornum[20];//记录素因子个数 int n=0; dp[0][0]=1,dp[0][1]=1; for(int i=2;i<MAX;++i){ int k=0,x=i; for(int j=2;j*j<=x;++j){//求素因子个数 if(x%j == 0){ int num=0; while(x%j == 0){++num;x=x/j;} factornum[k++]=num; } } if(x != 1)factornum[k++]=1; int ans=1; for(int j=0;j<k;++j){//计算i的因子个数 ans*=(factornum[j]+1); } //不打表会超时,无奈只能打表 if(ans>dp[n][1]){dp[n+1][0]=i,dp[++n][1]=ans;cout<<dp[n][0]<<' '<<dp[n][1]<<endl;} } cout<<n<<endl; }*/ void BuildTree(int n,int left,int right){ sum[n]=right-left+1; if(left == right)return; int mid=left+right>>1; BuildTree(n<<1,left,mid); BuildTree(n<<1|1,mid+1,right); } int Query(int pos,int n,int left,int right){ --sum[n];//该区间的人将退出一个 if(left == right)return left; int mid=left+right>>1; if(sum[n<<1]>=pos)return Query(pos,n<<1,left,mid); else return Query(pos-sum[n<<1],n<<1|1,mid+1,right); } int main(){ //Factor(); int n,k; while(scanf("%d%d",&n,&k)!=EOF){ for(int i=1;i<=n;++i){ scanf("%s%d",name[i],&s[i]); } BuildTree(1,1,n); int m=34,pos=k,num=n;//m确定退出的序号,pos确定退出的人位置序号 int x=k-1;//x表示pos前面剩余几个数 k=1; for(int i=0;i<=34;++i)if(n<dp[i][0]){m=i-1;break;} Query(pos,1,1,n); while(k != dp[m][0]){ --num;//总共剩余的人的个数 ++k;//准备退出的人是第几个退出的 if(s[pos]>0)pos=(x+s[pos])%num; else pos=((x+s[pos]+1)%num+num)%num; if(pos == 0)pos=num; x=pos-1; pos=Query(pos,1,1,n);//查询这个位置序号 } printf("%s %d\n",name[pos],dp[m][1]); } return 0; }

简洁版


#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<queue>
#include<algorithm>
#include<map>
#include<iomanip>
#define INF 99999999
using namespace std;

const int MAX=500000+10;
int sum[MAX<<2];//表示区间内剩余的人数
int dp[35][2]={
    {1,1},{2,2},{4,3},{6,4},{12,6},{24,8},{36,9},{48,10},{60,12},{120,16},{180,18},{240,20},{360,24},{720,30},
    {840,32},{1260,36},{1680,40},{2520,48},{5040,60},{7560,64},{10080,72},{15120,80},{20160,84},{25200,90},{27720,96},
    {45360,100},{50400,108},{55440,120},{83160,128},{110880,144},{166320,160},{221760,168},{277200,180},{332640,192},{498960,200}
};
char name[MAX][12];
int s[MAX];

void BuildTree(int n,int left,int right){
    sum[n]=right-left+1;
    if(left == right)return;
    int mid=left+right>>1;
    BuildTree(n<<1,left,mid);
    BuildTree(n<<1|1,mid+1,right);
}

int Query(int pos,int n,int left,int right){
    --sum[n];
    if(left == right)return left;
    int mid=left+right>>1;
    if(sum[n<<1]>=pos)return Query(pos,n<<1,left,mid);
    else return Query(pos-sum[n<<1],n<<1|1,mid+1,right);
}

int main(){
    int n,k;
    while(cin>>n>>k){
        for(int i=1;i<=n;++i){
            scanf("%s%d",name[i],&s[i]);
        }
        BuildTree(1,1,n);
        int m=34;//n>MAX则最多约数在dp[34]这个位置
        for(int i=0;i<35;++i)if(n<dp[i][0]){m=i-1;break;}
        int pos=k,x=k-1,num=n-1;//pos表示下一个要退出的人的位置,x表示下一个要退出的人的前面(1~pos)有几个人
        Query(pos,1,1,n);
        while(n-num != dp[m][0]){
            if(s[pos]>0)pos=(x+s[pos])%num;//下一个位置相对x顺时针增加
            else pos=((x+s[pos]+1)%num+num)%num;//下一个位置相对x逆时针减少
            if(pos == 0)pos=num;
            x=pos-1;
            pos=Query(pos,1,1,n);//查询pos位置的人并且删除这个人
            --num;//总人数减1
        }
        printf("%s %d\n",name[pos],dp[m][1]);
    }
    return 0;
}


























  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值