传送门:http://poj.org/problem?id=2886
这题真是太厉害了。。
我们要先知道反素数,推荐: http://blog.csdn.net/ACdreamers/article/details/25049767
这题很多人都是打表反素数的,我暴力预处理了一下,虽然有点慢,最后也是4000+ms过的。。
这题的答案一定为反素数,因为小于这个反素数的数的因子数一定比反素数少,所以求出<=n的最大的反素数,就一定是1-n中因子数最多的数字。知道这个结论以后,用线段树模拟一下约瑟夫环的跳转过程。
if(val[pos]>0){
k=(((k-1+val[pos]-1)%mod)+mod)%mod+1;
//k-1表示去掉本身,val[pos]-1在后面+1回来是为了防止k=0
}
else{
k=(((k+val[pos]-1)%mod)+mod)%mod+1;
//往前找的时候k不用去掉,所以k不用-1
}
然后每次把k更新到线段树里,类似于POJ 2828 排队那题
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <string>
#include <iostream>
#include <vector>
#include <map>
#include <set>
#include <queue>
#include <ctime>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
#define pb push_back
#define mp make_pair
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define calm (l+r)>>1
const int INF = 1e9+7;
const int maxn=500010;
bool vis[110];
vector<int> prime;
int antiprime[300],num[300];
void init(){//打表反素数
for(int i=2; i<=50; i++){
if(!vis[i]){
prime.pb(i);
for(int j=i+i; j<=50; j+=i){
vis[j]=true;
}
}
}
memset(antiprime,127,sizeof antiprime);
antiprime[1]=1;
for(int i=1;i<=maxn;i++){
int ans=1;
for(int j=0,len=prime.size(); j<len; j++){
if(i%prime[j]==0){
int tot=1,t=i;
while(t%prime[j]==0){
t/=prime[j];tot++;
}
ans*=tot;
}
antiprime[ans]=min(antiprime[ans],i);
}
}
sort(antiprime,antiprime+300);
for(int i=0;i<300;i++){
if(antiprime[i]==antiprime[299])break;
int ans=1;
for(int j=0,len=prime.size(); j<len; j++){
if(antiprime[i]%prime[j]==0){
int tot=1,t=antiprime[i];
while(t%prime[j]==0){
t/=prime[j];tot++;
}
ans*=tot;
}
}
num[i]=ans;
}
int now=0;
for(int i=0;i<300;i++){
if(antiprime[i]==antiprime[299]){break;}
if(num[i]<=now)antiprime[i]=antiprime[299];
now=max(now,num[i]);
}
sort(antiprime,antiprime+300);
for(int i=0;i<300;i++){
if(antiprime[i]==antiprime[299]){break;}
}
for(int i=0;i<300;i++){
if(antiprime[i]==antiprime[299])break;
int ans=1;
for(int j=0,len=prime.size(); j<len; j++){
if(antiprime[i]%prime[j]==0){
int tot=1,t=antiprime[i];
while(t%prime[j]==0){
t/=prime[j];tot++;
}
ans*=tot;
}
}
num[i]=ans;
}
}
int n,k;
char name[maxn][13];
int val[maxn];
int sum[maxn<<2];
void build(int l,int r,int rt){
sum[rt]=r-l+1;
if(l==r){
return;
}
int m=calm;
build(lson);build(rson);
}
int update(int x,int l,int r,int rt){
sum[rt]--;
if(l==r)return l;
int m=calm;
if(x<=sum[rt<<1])return update(x,lson);
return update(x-sum[rt<<1],rson);
}
int main()
{
//freopen("D://input.txt","r",stdin);
init();
while(scanf("%d%d",&n,&k)!=EOF){
for(int i=1;i<=n;i++){
scanf("%s%d",name[i],&val[i]);
}
int cnt=0;
for(int i=0;i<35;i++){
//答案一定为反素数,因为小于这个反素数的数的因子数一定比反素数少
if(antiprime[i]<=n)cnt=i;
}
//printf("%d\n",antiprime[cnt]);
build(1,n,1);
int &mod=sum[1];//圈内总人数
int pos=0;
val[0]=0;//先等于0,使第一个人为k
for(int i=0;i<antiprime[cnt];i++){
if(val[pos]>0){
k=(((k-1+val[pos]-1)%mod)+mod)%mod+1;
//k-1表示去掉本身,val[pos]-1在后面+1回来是为了防止k=0
}
else{
k=(((k+val[pos]-1)%mod)+mod)%mod+1;
//往前找的时候k不用去掉,所以k不用-1
}
pos=update(k,1,n,1);
}
printf("%s %d\n",name[pos],num[cnt]);
}
return 0;
}