首页 > 代码库 > poj 2886 线段树的更新+反素数
poj 2886 线段树的更新+反素数
Who Gets the Most Candies?
Time Limit: 5000 MS Memory Limit: 0 KB
64-bit integer IO format: %I64d , %I64u Java class name: Main
[Submit] [Status] [Discuss]
Description
N children are sitting in a circle to play a game.
The children are numbered from 1 to N in clockwise order. Each of them has a card with a non-zero integer on it in his/her hand. The game starts from the K-th child, who tells all the others the integer on his card and jumps out of the circle. The integer on his card tells the next child to jump out. Let A denote the integer. If A is positive, the next child will be the A-th child to the left. If A is negative, the next child will be the (−A)-th child to the right.
The game lasts until all children have jumped out of the circle. During the game, the p-th child jumping out will get F(p) candies where F(p) is the number of positive integers that perfectly divide p. Who gets the most candies?
Input
Output
Output one line for each test case containing the name of the luckiest child and the number of candies he/she gets. If ties occur, always choose the child who jumps out of the circle first.
Sample Input
4 2Tom 2Jack 4Mary -1Sam 1
Sample Output
Sam 3
if
(val[pos]>=0)
//顺时针
k = (k-1+val[pos]-1)%n+1;
else
//逆时针
k = ((k-1+val[pos])%n+n)%n+1;
#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 500000const 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;
lldtemp=cur;
for(inti=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(inti=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]);
}
return0;
}
zoj 2562
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
typedef long long lld;
lldprime[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;
lldtemp=curr;
for(inti=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);
}
return0;
}