#include <stdio.h>
#include <string.h>
#define L(x) (x<<1)
#define R(x) (x<<1|1)
const int M = 500010;
int n,id;
struct tree
{
int l;
int r;
int sum; //sum 表 该区间剩余人数
} node[M*3];
struct data
{
int val;
char name[15];
} pp[M];
int ans[M]; //ans[i]保存第i个人跳出能得到的糖果数量
void Build (int l,int r,int root)
{
node[root].l = l;
node[root].r = r;
node[root].sum = r - l + 1;
if (l == r)
return ;
int mid = (l + r)>>1;
Build(l,mid,root*2);
Build(mid+1,r,root*2+1);
}
int update (int key,int root)
{
node[root].sum --;
if (node[root].l == node[root].r)
return node[root].l;
if (node[root*2].sum >= key)
return update(key,root*2);
else
return update (key - node[root*2].sum,root*2+1);
}
void count_ans() ///n人中 第几个跳出来的人获得最多 ///反素数
{
memset (ans,0,sizeof(ans)); //计算ans
for (int i = 1; i <= n; i ++)
{
ans[i] ++; ///均==1了
for (int j = 2*i; j <= n; j += i)
ans[j] ++; ///ans[2]=2 ans[3]=2 ans[4]=2 ans[4]=3 ......
}
int max = ans[1];
id = 1;
for (int i = 2; i <= n; i ++) //找出第几个人跳出获得的糖最多
if (ans[i] > max)
{
max = ans[i];
id = i;
}
}
int main ()
{
int i,k,mod;
while (~scanf ("%d %d",&n,&k))
{
count_ans();
for (i = 1; i <= n; i ++)
scanf ("%s %d",pp[i].name,&pp[i].val);
Build (1,n,1);
mod = node[1].sum;
int pos = 0;
pp[0].val = 0;
n = id;
while (n --)
{
if (pp[pos].val > 0) //k表剩余的人中从左起第k中出队(PS:k的求法是看别人的)
k = ((k + pp[pos].val - 2)%mod + mod)%mod + 1;
else
k = ((k + pp[pos].val - 1)%mod + mod)%mod + 1;
pos = update(k,1);
mod = node[1].sum; ///不断更新后的结果
}
printf ("%s %d\n",pp[pos].name,ans[id]);
}
return 0;
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;
#define MAX 500000
const int antiprime[] = {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,554400};
const int factor[] = {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,216};
struct node
{
int l;
int r;
int cnt;
} tr[3*MAX];
struct p
{
char name[25];
int v;
} pp[MAX];
int build(int l,int r,int root)
{
tr[root].l=l;
tr[root].r=r;
int mid=(l+r)/2;
tr[root].cnt=tr[root].r-tr[root].l+1;
if(l!=r)
{
build(l,mid,root*2);
build(mid+1,r,root*2+1);
}
}
int update(int root,int k)
{
tr[root].cnt--;
if(tr[root].l==tr[root].r)
return tr[root].l;
if(tr[root*2].cnt >= k)
return update(root*2, k);
else
return update(root*2+1, k - tr[root*2].cnt);
}
int main()
{
int n,k;
while(scanf("%d%d",&n,&k)!=EOF)
{
for(int i=1; i<=n; i++)
scanf("%s%d",&pp[i].name,&pp[i].v);
build(1,n,1); ///建树
int cnt=0;
while(antiprime[cnt]<=n) cnt++;
cnt--;
int pos=0;
pp[pos].v=0;
for(int i=0; i<antiprime[cnt]; i++)
{
if(pp[pos].v > 0) ///顺时针
{
k = ((k + pp[pos].v - 2) % tr[1].cnt + tr[1].cnt) % tr[1].cnt + 1;
}
else ///逆时针
{
k = ((k + pp[pos].v - 1) % tr[1].cnt + tr[1].cnt) % tr[1].cnt + 1;
}
pos = update(1, k); ///更新一下
}
printf("%s %d\n", pp[pos].name, factor[cnt]);
}
return 0;
}
*******
定义 反素数:
问题描述:
对于任何正整数x,起约数的个数记做g(x).例如g(1)=1,g(6)=4.
如果某个正整数x满足:对于任意i(0<i<x),都有g(i)<g(x),则称x为反素数.
现在给一个N,求出不超过N的最大的反素数.
比如:输入1000 输出 840
思维过程:
求[1..N]中约数在大的反素数-->求约数最多的数
如果求约数的个数 756=2^2*3^3*7^1
(2+1)*(3+1)*(1+1)=24
基于上述结论,给出算法:按照质因数大小递增顺序搜索每一个质因子,枚举每一个质因子
为了剪枝:
性质一:一个反素数的质因子必然是从2开始连续的质数.
因为最多只需要10个素数构造:2,3,5,7,11,13,17,19,23,29
性质二:p=2^t1*3^t2*5^t3*7^t4.....必然t1>=t2>=t3>=....
(以上摘自百度百科)
以hdu4228 和zoj2562为例
hdu4228
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
typedef __int64 lld;
lld p[1010];
lld prime[30]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53};
void getartprime(lld cur,int cnt,int limit,int k)
{
//cur:当前枚举到的数;
//cnt:该数的因数个数;
//limit:因数个数的上限;2^t1*3^t2*5^t3……t1>=t2>=t3……
//第k大的素数
if(cur>((lld)1<<60) ||
cnt>150) return ;
if(p[cnt]!=0
&&
p[cnt]>cur)//当前的因数个数已经记录过且当时记录的数比当前枚举到的数要大,则替换此因数个数下的枚举到的数
p[cnt]=cur;
if(p[cnt]==0)//此因数个数的数还没有出现过,则记录
p[cnt]=cur;
lld
temp=cur;
for(int
i=1;i<=limit;i++)//枚举数
{
temp=temp*prime[k];
if(temp>((lld)1<<60))
return;
getartprime(temp,cnt*(i+1),i,k+1);
}
}
int main()
{
int n;
getartprime(1,1,75,0);
for(int
i=1;i<=75;i++)
{
if(p[i*2-1]!=0 && p[i*2]!=0)
p[i]=min(p[i*2-1],p[i*2]);
else if(p[i*2]!=0)
p[i]=p[i*2];
else
p[i]=p[i*2-1];
}
while(scanf("%d",&n),n)
{
printf("%I64d\n",p[n]);
}
return
0;
}
zoj 2562
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
typedef long long lld;
lld
prime[20]={2,3,5,7,11,13,17,19,23,29,31,37,39,41,43,47,53};
lld n;
lld bestcurr,largecnt;//bestcurr
相同最大因数个数中值最小的数,largecnt:n范围内最大的因数个数
void getarcprime(lld curr,int cnt,int limit,int k)
{
if(curr>n)
return ;
if(largecnt<cnt)//此时枚举到的因数个数比之前记录的最大的因数个数要大,就替换最大因数个数
{
largecnt=cnt;
bestcurr=curr;
}
if(largecnt==cnt &&
bestcurr>curr)//替换最优值
bestcurr=curr;
lld
temp=curr;
for(int
i=1;i<=limit;i++)
{
temp=temp*prime[k];
if(temp>n)
return;
getarcprime(temp,cnt*(i+1),i,k+1);
}
}
int main()
{
while(scanf("%lld",&n)!=EOF)
{
bestcurr=0;
largecnt=0;
getarcprime(1,1,50,0);
printf("%lld\n",bestcurr);
}
return
0;
}