poj2886-Who Gets the Most Candies?(线段树)
题目链接:http://poj.org/problem?id=2886
又是一个圈,然后又删人...不过,删人有规则,有顺时针又有逆时针,然后问你n个删除的人中第i个被删除人的i约数个数最大值是多少.
数据范围达到50 0000 ,有点多了,所以,一般这样可以打表...
打一个反素数表,对于n个人来说,它的最大约数个数就等于表中<=n的书中最大的反素数m约数个数;
最后就是如何输出第m个人的名字,其实还是采用模拟法,为了减少时长,我们才采用了线段树
此处,线段树的random域依然是记录【i , j】的空位个数
期间遇到Runtime Error —— 我们知道 i == j 是线段树结点,可是我在每进行这一判断的时候,竟然直接默认它为父节点,然后就出错了
虽然它折磨我许久,不过很开心,我学会独立思考random域如何运用了,也没看模板,自行思考解决
代码如下:
#include
#include
#include
#include
#define maxn 500050
struct node
{
int l , r ;
int leftnum ;
}a[maxn*4];
int leftsum ;/*the sum of left numbers*/
int next[maxn] ;
char name[maxn][15];
int maxperson[40] = {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 maxvalue[40] = {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,1314521};
void init(int le , int ri , int i , int k )
{
int mid;
a[i].l = le ;
a[i].r = ri ;
a[i].leftnum = ri-le+1 ;
if( le <= k && k <= ri ) a[i].leftnum -- ;
if( a[i].l == a[i].r ) return;
mid = ( a[i].l+a[i].r )/2 ;
init( le , mid , 2*i , k) ;
init( mid+1 , ri , 2*i+1 , k) ;
}
int getnum( int le , int re , int i )
{///专门用来得到对任一位置loc,1-loc-1 以及 loc+1 - n的空位个数
int mid = ( a[i].l + a[i].r )/2 ;
if( le > re ) return 0 ;
///一开始这步错了,其实范围符合就可以直接返回了
if( a[i].l == le && re == a[i].r ) return a[i].leftnum ;
if( le > mid )
{
return getnum( le , re , 2*i+1 );
}
else if( re <= mid )
{
return getnum ( le , re , 2*i );
}
else
{
return getnum(le , mid , 2*i) + getnum(mid+1 , re , 2*i+1) ;
}
}
int getans( int insert , int i )
{///找到插入位,然后插入就好了
int m ;
int leftt = a[i].l ;
int rightt = a[i].r ;
int mid ;
if( leftt == rightt )
{
a[i].leftnum = 0 ;
return a[i].l ;
}
m = a[2*i].leftnum;///在使用2i 的时候,一定要保证left != right
if( insert <= m )
{
a[i].leftnum -- ;
return getans( insert , 2*i ) ;
}
else
{
a[i].leftnum -- ;
return getans( insert - m , 2*i+1 ) ;
}
}
int main()
{
int i ; int p; int star ;
int n , k ; int per ; int loc ;
int lef ; int rig ;
while( scanf( "%d%d", &n , &k ) != EOF )
{
leftsum = n ;
for( i = 1; i <= n ; i ++ )
{
scanf( "%s%d", name[i] , &next[i] ) ;
}
for( p = 0 ; p < 40 ; p++ )
{
if( maxperson[p] > n )
break ;
}
per = p-1;
p = maxperson[per];
///一定要初始化
init( 1 , n , 1 , k );
leftsum -- ;
loc = k ; k = next[k] ;
if(leftsum)
{
for( i = 1 ; i < p ; i++ )
{
lef = getnum( 1 , loc - 1 , 1 );
rig = leftsum - lef ;
/// k 的情况还挺多,不过如果能化为一种就很好了,我把k的值全部转化为从1开始要插入的第k个空位
if( k > 0 )
{
k = k + lef ;
k = k % leftsum == 0 ? leftsum : k%leftsum ;
}
else
{
k = 0 - k ;
k = k + rig ;
k = k % leftsum == 0 ? leftsum : k%leftsum ;
k = leftsum - k + 1 ;
}
loc = getans( k , 1 ) ;
leftsum -- ;
if(!leftsum) break;
k = next[loc] ;
}
}
printf( "%s %d\n" , name[loc] , maxvalue[per] ) ;
}
return 0;
}