有 N个人顺时针围成一圈,刚开始第 K个人退出圈,他手上有个数字 A_k
如果 A_k为正,则他左数 A_k个人退出,如果 A_k为负,则他右数 A_k个人退出
同样,接下来退出的那个人手上也有个数字,一直进行下去直到圈内所有人都离开
第 p个人退出的时候,他能得到 F(p)个糖果,F(p) 为 p约数的个数
问得到最多糖果的最早退出的人是谁,他得到了几个糖果
这是个很经典的问题,叫做 Josep环问题,以前我只会模拟求
虽然现在也只会模拟求但利用树状数组,可以使得这个过程变得很高效
维护一个树状数组,每个点存的是每个人的现在的序号
当第 i个人退出时,其后所有人向前移一位,序号减一 ( bit.add(i,-1) )
然后总人数再减一,并计算下一个人在新的环中的序号
下一次在树状数组中二分查找这个序号的下界,然后循环
用样例来说明,现在有四个人:1 2 3 4
第一次第二个人退出后,第二位以后减一:1 1 2 3
然后下一个人的序号为 (1+4)%3=2
二分查找这个人在树状数组中的位置,即为第 3个
然后重复这个过程:1 1 1 2
关于题目提到的要求的答案,其实就是不超过 N的最大的反素数
所以其实可以先把反素数 M求出来,然后进行到第 M次就行了,不必模拟到底
由于树状数组从 1开始标号,所以取模的时候有诸多不便
再加上每个人的偏移量会有负数,所以存在一些坑点
1) 当偏移量为负数时,要加上 1
2) 取模会有负数,要 +tot然后再 %tot
3) 如果模的结果为 0,要手动变为 tot
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef pair<int,int> Pii;
typedef long long LL;
typedef unsigned long long ULL;
typedef double DBL;
typedef long double LDBL;
#define MST(a,b) memset(a,b,sizeof(a))
#define CLR(a) MST(a,0)
#define Pow2(a) (a*a)
const int maxn=5e5+10;
const int tprm[16]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53};
struct BIT
{
int *Bit;int siz;
BIT(int size):siz(size){Bit=new int[size+1];memset(Bit,0,sizeof(int)*(size+1));}
~BIT(){delete []Bit;}
int lowbit(int x){return x&-x;}
void add(int x, int v)
{
while(x<=siz)
{
Bit[x]+=v;
x+=lowbit(x);
}
}
int sum(int x)
{
int res=0;
while(x>0) // Indexed start from 1
{
res+=Bit[x];
x-=lowbit(x);
}
return res;
}
void update(int x,int v){add(x,v);add(x+1,-v);}
void update(int l,int r,int v){add(l,v);update(r+1,-v);}
int query(int x){return sum(x);}
};
int N,K;
char name[maxn][15];
int nxt[maxn];
LL aprm,aprmc;
void get_aprm(int,int,LL,LL,LL); //anti_prime
int main()
{
bool first=1;
while(~scanf("%d%d", &N, &K))
{
BIT bit(N);
for(int i=1; i<=N; i++)
{
scanf(" %s %d", name[i], &nxt[i]);
bit.update(i,i);
}
aprm=1e18,aprmc=0;
get_aprm(0,63,1,1,N);
int ans=aprmc,M=aprm,p=K,tot=N,l=K,r;
for(int i=1; i1);
if(nxt[l]<0) nxt[l]+=1;
p=((bit.query(l)+nxt[l])%tot+tot)%tot;
if(p==0) p=tot;
l=1,r=N;
while(lint mid=(l+r)>>1;
if(bit.query(mid)>=p) r=mid;
else l=mid+1;
}
}
printf("%s %d\n", name[l], ans);
}
return 0;
}
void get_aprm(int np,int top,LL fcnt,LL now,LL LimN)
{
if(fcnt>aprmc){aprm=now;aprmc=fcnt;}
if(fcnt==aprmc&&nowif(np>=16) return;
for(int i=1; i<=top; i++)
{
if(now>LimN/tprm[np]) break;
now*=tprm[np];
get_aprm(np+1,i,fcnt*(i+1),now,LimN);
}
}