POJ2886 线段树+约瑟夫 xingxingz在努力

  这道题算是线段树和约瑟夫结合的比较好的题了, 题目给你n个顺时针站的人,每个人手里拿着一个数字m, 刚开始第k个人自杀,然后拿到当前自杀的人所拿的数字, 如果大于0则从当前自杀的人的下一位开始顺时针数,第 m个人自杀, 否则的话逆时针第m个人自杀, 假设当前的人是第i个自杀的人, 那么他会得到i的约数的个数个糖果?思路大体如下, 对于一个特定的n我们求出1-n中约数个数最多的那个数, 然后用线段树模拟自杀过程即可。。假设上一次是第k个人自杀, 如果这个人手持的数字m大于0那么这一次应该是第((k-1+m-1)%n+mod)%mod+1,mod是当前的人数, 假设m小于0那么这一次应该是第((k+m-1)%mod+mod)%mod+1 同理mod是当前的活着的人数。。代码如下:

 

  

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
int N, K;

int p[16] = {2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53};
int res, res_num;
void dfs(int dept, int tmp, int num)  //tmp有num个约数
{
    if(dept >= 16) return ;
    if(num > res_num) res_num=num, res=tmp;
    else if(num==res_num&&tmp<res) res=tmp;

    for(int i=1; i<=63; i++)
    {
        if(tmp*p[dept] > N)  break;
        dfs(dept+1, tmp*=p[dept], num*(i+1));
    }
}

struct PEO
{
    char name[11];
    int m;
}peo[500000 + 10];

struct segment
{
    int l, r;
    int x;         //区间内的人数
}seg[3*500000+100];

void build(int rt, int l, int r)
{
    seg[rt].l = l;
    seg[rt].r = r;
    seg[rt].x = r-l+1;
    if(l==r) return;
    int chl = 2*rt, chr = 2*rt+1;
    build(chl, l, (l+r)/2);
    build(chr, (l+r)/2+1, r);
}

int query(int rt,int p)
{
    seg[rt].x--;
    if(seg[rt].l==seg[rt].r)
        return seg[rt].l;
    int chl=2*rt, chr=2*rt+1;

    if(seg[chl].x>=p)
        return query(chl, p);
    else
        return query(chr, p-seg[chl].x);;
}

int main()
{
    while(scanf("%d%d", &N, &K) == 2)
    {
        for(int i=1; i<=N; i++)
            scanf("%s%d", peo[i].name, &peo[i].m);
        res_num=0;
        dfs(0, 1, 1);
        //printf("%d %d\n", res, res_num);
        build(1, 1, N);
        int pos = 0;
        peo[0].m = 0;
        int &mod = seg[1].x;
        //printf("%d %d\n", s, nt);
        for(int i=0; i<res; i++)
        {
            //printf("mod = %d\n", mod);
            if(peo[pos].m > 0)
                K = ((K+peo[pos].m-2)%mod+mod)%mod + 1;
            else
                K = ((K+peo[pos].m-1)%mod+mod)%mod + 1;
            pos = query(1, K);
            //printf("%d个人是%s\n", i+2, peo[s].name);
        }
        printf("%s %d\n", peo[pos].name, res_num);
    }
    return 0;
}

 树状数组解法:

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
int N, K;

struct Person
{
    char name[12];
    int m;
}per[500000 + 100];

//反素数部分代码
int p[16] = {2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53};
int _n;     //1-_n范围内
int res, res_num;
void dfs(int dept, int tmp, int num)   //深度为dept tmp有num个约数
{
    if(dept >= 16)  return ;
    if(num > res_num) res_num=num, res = tmp;
    else if(num==res_num&&tmp<res) res = tmp;

    for(int i=1; i<=63; i++)
    {
        if(tmp*p[dept] > _n) break;
        dfs(dept+1, tmp*=p[dept], num*(i+1));
    }
}

void getmost(int N, int &rs, int &rs_num)    //在1-N中找到一个数, 这个数的约数个数最多。。
{
    _n = N;
    res_num = 0;
    dfs(0, 1, 1);
    rs = res; rs_num = res_num;
    return ;
}

//树状数组部分代码
int c[500000 + 100];

int lowbit(int x)
{
    return x&(-x);
}
int sum(int i)
{
    int s = 0;
    while(i > 0)
    {
        s += c[i];
        i -= lowbit(i);
    }
    return s;
}
void add(int i, int val)
{
    while(i <=N)
    {
        c[i] += val;
        i += lowbit(i);
    }
}

int main()
{
    while(scanf("%d%d", &N, &K) == 2)
    {
        for(int i=1; i<=N; i++)
            scanf("%s%d", per[i].name, &per[i].m);
        int n, num;   //1-N中 n有num个约数
        getmost(N, n, num);
        //printf("%d has %d factor\n", n, num);
        memset(c, 0, sizeof(c));
        for(int i=1; i<=N; i++)
            add(i, 1);
        int idx = 0;
        for(int i=0; i<n; i++)
        {
            int l=1, r=N;
            while(l <= r)
            {
                int mid = (l+r)/2;
                if(sum(mid)>=K)
                {
                    idx = mid;
                    r = mid-1;
                }
                else 
                    l = mid + 1;
            }
            //printf("idx = %d\n", idx);  ///
            add(idx, -1);
            int mod = sum(N);
            int tmp = per[idx].m;
            //printf("mod = %d\n", mod);    ///
            if(mod)
            {
                if(tmp > 0)
                    K = ((K-1+tmp-1)%mod+mod)%mod + 1;
                else 
                    K = ((K+tmp-1)%mod+mod)%mod + 1;
            }
        }
        printf("%s %d\n", per[idx].name, num);
    }
    return 0;
}

 

posted @ 2015-11-19 22:58  xing-xing  阅读(117)  评论(0)    收藏  举报