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

There are several test cases in the input. Each test case starts with two integers N (0 < N ≤ 500,000) and K (1 ≤ KN) on the first line. The next N lines contains the names of the children (consisting of at most 10 letters) and the integers (non-zero with magnitudes within 108) on their cards in increasing order of the children’s numbers, a name and an integer separated by a single space in a line with no leading or trailing spaces.

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 2
Tom 2
Jack 4
Mary -1
Sam 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 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;
}


poj 2886 线段树的更新+反素数

时间: 2024-08-28 13:07:19

poj 2886 线段树的更新+反素数的相关文章

POJ 2886 线段树单点更新

转载自:http://blog.csdn.net/sdj222555/article/details/6878651 反素数拓展参照:http://blog.csdn.net/ACdreamers/article/details/25049767 题目大意就是一群熊孩子做游戏,第一个出队的人是编号为k的人.此后出队的人就是按照前一个人手里的编号.如果是正数+m就是这个人的左边的第m个人.如果是负数-m,就是 这个人的右边第m个人.由于这个人出队了.对下一个人有影响,所以+m的时候,是k+m-1.

poj 2886 Who Gets the Most Candies?(线段树+约瑟夫环+反素数)

Who Gets the Most Candies? Time Limit: 5000MS   Memory Limit: 131072K Total Submissions: 9934   Accepted: 3050 Case Time Limit: 2000MS Description N children are sitting in a circle to play a game. The children are numbered from 1 to N in clockwise o

poj 2886 (线段树+反素数打表) Who Gets the Most Candies?

http://poj.org/problem?id=2886 一群孩子从编号1到n按顺时针的方向围成一个圆,每个孩子手中卡片上有一个数字,首先是编号为k的孩子出去,如果他手上的数字m是正数,那么从他左边(顺时针)开始第m个孩子出去,如果是负的 那么从他的右边(也就是逆时针)开始第m个孩子出去~~~一直到所有的孩子出去,另外,第p个出去的孩子可以得到的糖果数量是p的约数个数,问能得到最多糖果的孩子的名字和得到的糖果数目 关于公约数最多的问题,可以利用到反素数,可以首先先打表反素数和对应的约数个数,

poj 2828(线段树单点更新)

Buy Tickets Time Limit: 4000MS   Memory Limit: 65536K Total Submissions: 18561   Accepted: 9209 Description Railway tickets were difficult to buy around the Lunar New Year in China, so we must get up early and join a long queue… The Lunar New Year wa

hdu 1698+poj 3468 (线段树 区间更新)

http://acm.hdu.edu.cn/showproblem.php?pid=1698 这个题意翻译起来有点猥琐啊,还是和谐一点吧 和涂颜色差不多,区间初始都为1,然后操作都是将x到y改为z,注意 是改为z,不是加或减,最后输出区间总值 也是线段树加lazy操作 1 #include<cstdio> 2 using namespace std; 3 struct point { 4 int l,r; 5 int val,sum; 6 }; 7 point tree[400007]; 8

poj 2528 线段树+延迟更新

题目链接:http://poj.org/problem?id=2528 题意: 在墙上贴海报,输入n(1<=n<=10000),表示n张海报,后n行输入 两整数l,r  ( 1<= l, r<= 1e9 ),表示海报从编号为l的石头一直贴到编号为r的石头,输入顺序即为粘贴顺序.问n张贴完之后,还能看到多少张海报. 思路: 显然区间操作,很容易联想到线段树操作,只不过区间 l,r 最大范围可达1e9,直接建树,内存必爆.   那么就需要避开1e9的数据,进行离散化,将区间变成(1到n

POJ 2828 (线段树 单点更新) Buy Tickets

倒着插,倒着插,这道题是倒着插! 想一下如果 Posi 里面有若干个0,那么排在最前面的一定是最后一个0. 从后往前看,对于第i个数,就应该插在第Posi + 1个空位上,所以用线段树来维护区间空位的个数. 说一下那个坑爹的第56行的判断: if(i > 1) printf(" "); 将输出的n个数用空格隔开,我感觉这是一个还算常用的写法啊,结果各种莫名TLE,加上输入挂也补救不回来. 去掉这个无谓的判断后,3594MS险过,加上输入挂3094MS,还算是起到了一定的加速作用.

POJ - 2886线段树+线性筛

题意就是给你n个人,每个人有一个名字和A,如果A为正则向左找第A个,否则向右找第-A个,每找到一个就出列,然后第i个人出列的F(i)是i的所有因子个数 输出最大的F(i)和对应的名字 先用线性筛找出第几个出列的值最大maxn,那么求到maxn就可以了. 用线段树记录空位,k表示当前需要从剩余人第几个位置寻找,只要推出k,则转化为线段树的求位置的模型 #include<iostream> #include<cstdio> #include<cstring> using n

POJ 3225 (线段树 区间更新) Help with Intervals

这道题搞了好久,其实坑点挺多.. 网上找了许多题解,发现思路其实都差不多,所以就不在重复了. 推荐一篇比较好的题解,请戳这. 另外,如果因为可能要更新多次,但最终查询只需要一次,所以没有写pushup函数,仅有一个pushdown. 1 #include <cstdio> 2 3 const int maxn = 131072; 4 //const int maxn = 10; 5 int qL, qR, op; 6 int setv[maxn << 2], xorv[maxn &