傳送門: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;
}