POJ 2886 Who Gets the Most Candies? (三种姿势点草动态求第k大)

Who Gets the Most Candies?

题意: 

N个孩子顺时针坐成一个圆圈且从1到N编号,每个孩子手中有一张标有非零整数的卡片。第K个孩子先出圈,如果他手中卡片上的数字A大于零,下一个出圈的是他左手边第A个孩子。否则,下一个出圈的是他右手边第(-A)个孩子。第p个出圈的孩子会得到F(p)个糖果,F(p)为p的因子数。求得到糖果数最多的是哪个孩子及得到多少糖果。

思路:

模拟约瑟夫环的感觉 注意处理取模就好了 最好在纸上画一下

坑: 

从1开始的时候  - - 千万千万别忘记了取模成0的时候 其实是模数啊 - - (又让我想起了某一次无缘D题 差1s就是这个原因 刚交瞬间就发现了)

负数加模数国际惯例都懂的


树状数组(logn)^2查询做法:

附本题一个很棒的case

//
//  Created by TaoSama on 2015-06-06
//  Copyright (c) 2015 TaoSama. All rights reserved.
//
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>

#define debug(x) printf(#x": %d\n", x);

using namespace std;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
const int N = 5e5 + 10;

int n, k, d[N];

void init() {
    for(int i = 1; i <= 5e5; ++i) d[i] = 1;
    for(int i = 2; i <= 5e5; ++i)
        for(int j = i; j <= 5e5; j += i)
            d[j]++;
}

int b[N];

void add(int i, int x) {
    for(; i <= 5e5; i += i & -i) b[i] += x;
}

int sum(int i) {
    int ret = 0;
    for(; i; i -= i & -i) ret += b[i];
    return ret;
}

int binSearch(int k) {
    int l = 1, r = 5e5;
    while(l <= r) {
        int m = l + r >> 1;
        if(sum(m) < k) l = m + 1;
        else r = m - 1;
    }
    return l;
}

int a[N];
char name[N][15];

int main() {
#ifdef LOCAL
    freopen("in.txt", "r", stdin);
//  freopen("out.txt","w",stdout);
#endif
    //ios_base::sync_with_stdio(0);

    init();
    while(scanf("%d%d", &n, &k) == 2) {
        for(int i = 1; i <= n; ++i) {
            scanf("%s%d", name[i], a + i);
            add(i, 1);
        }
        int ans, p = k, Max = -INF;  //p真实位置 k名次
        for(int i = 1; i <= n; ++i) {
            add(p, -1);
            if(d[i] > Max) Max = d[i], ans = p;
            int mod = n - i;
            if(!mod) break;
//            cout << "mod: " << mod << " p: " << p << ' ' << sum(p) << endl;
            if(a[p] > 0) k = (sum(p) + a[p]) % mod;
            else k = ((sum(p) + a[p]) % mod + mod) % mod + 1;
//            cout << ((sum(p) + a[p]) % mod + mod) % mod << endl;
            if(k == 0) k = mod;
            p = binSearch(k);
//            cout << "k: " << k << " p: " << p << endl;
        }
        printf("%s %d\n", name[ans], Max);
    }
    return 0;
}

/*
7 3
a 3
b 2
c -5
d 4
e 8
f 2
g -6

ans: g 4
*/

线段树logn查询做法:

用到了反素数打表 - - 我会说光看懂这个东西就用了好几天

详情见 http://blog.csdn.net/lwt36/article/details/46526987

//
//  Created by TaoSama on 2015-06-11
//  Copyright (c) 2015 TaoSama. All rights reserved.
//
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>

using namespace std;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
const int N = 5e5 + 10;

typedef long long LL;

int p[] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53};
map<LL, LL> mp, rmp; //divisors .. anti-prime
LL a[100], b[100], ct;

void dfs(int k, LL cur, LL cnt, int limit) {
    if(cur > N) return;
    if(!mp.count(cnt)) mp[cnt] = cur;
    else mp[cnt] = min(cur, mp[cnt]);
    for(int i = 1; i <= limit; ++i) {
        if(1.0 * cur * p[k] > N) break;
        dfs(k + 1, cur *= p[k], cnt * (i + 1), i);
    }
}

void init() {
    dfs(0, 1, 1, 60);
    map<LL, LL>::iterator i = mp.begin();
    for(; i != mp.end(); ++i) rmp.insert(make_pair(i->second, i->first));
    i = rmp.begin();
    for(; i != rmp.end(); ++i) {
        if(ct > 0 && i->second < b[ct - 1]) continue;
        a[ct] = i->first;
        b[ct++] = i->second;
    }
}

int n, k, v[N];
char name[N][10];

int sum[N * 3];

#define root 1, n, 1
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1

void build(int l, int r, int rt){
    sum[rt] = r - l + 1;
    if(l == r) return;
    int m = l + r >> 1;
    build(lson);
    build(rson);
}

int query(int k, int l, int r, int rt){
    sum[rt]--;
    if(l == r) return l;
    int m = l + r >> 1;
    if(k <= sum[rt << 1]) return query(k, lson);
    else return query(k - sum[rt << 1], rson);
}

int main() {
#ifdef LOCAL
    freopen("in.txt", "r", stdin);
//  freopen("out.txt","w",stdout);
#endif
    ios_base::sync_with_stdio(0);

    init();
    while(scanf("%d%d", &n, &k) == 2) {
        build(root);
        for(int i = 1; i <= n; ++i) scanf("%s%d", name[i], v + i);

        int ans, idx = 0;
        while(a[idx] <= n && idx < ct) idx++;
        int cnt = a[idx - 1], Max = b[idx - 1]; //第cnt个就是最大的

        for(int i = 1; i <= cnt; ++i) {
            int mod = n - i;
            ans = query(k, root);
            if(mod == 0) break;
            if(v[ans] > 0) k = (k - 1 + v[ans]) % mod;
            else k = ((k - 1 + v[ans]) % mod + mod) % mod + 1;
            if(k == 0) k = mod;
        }
        printf("%s %d\n", name[ans], Max);
    }
    return 0;
}

树状数组logn查询做法:

部分内容来自 http://www.cnblogs.com/wuyiqi/archive/2011/12/25/2301071.html


现在假设要求sum[x]的值,一般我们都是从后往前求和,如x=15
15-lowbit(15)=14;
14-lowbit(14)=12;
12-lowbit(12)=8;
8-lowbit(b)=0;
答案就是sum[15]+sum[14]+sum[12]+sum[8];

现在我们可以这样来求,从不超过15的只有一个1的最大二进制数,也可以理解为指数从log(15)取整=3开始

2^3=8, 加上2^2=4, 加上2^1=2, 加上2^0=1 数字依次为8,12,14,15,也就是把普通的求和过程反向。

int find_kth(int k) {
    int m = 31 - __builtin_clz(n);  //求32位log2(n)
    int cnt = 0, ret = 0;
    for(int i = m; i >= 0; --i) {
        ret += 1 << i;
        if(ret >= n || cnt + bit[ret] >= k) ret -= 1 << i;
        //可能会有很多个数都满足cnt+bit[ret]>=k 找的最大的ret满足cnt+bit[ret]<k
        else cnt += bit[ret];
        //cnt累加比当前ans小的总数
    }
    //ret是cnt(即小于等于ret的数的个数)小于k的情况的最大值 ret+1即第k大的数
    return ret + 1;
}



参考code:

//
//  Created by TaoSama on 2015-06-11
//  Copyright (c) 2015 TaoSama. All rights reserved.
//
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>

using namespace std;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
const int N = 5e5 + 10;

typedef long long LL;

int p[] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53};
map<LL, LL> mp, rmp; //divisors .. anti-prime
LL a[100], b[100], ct; //a: anti-prime b: its divisors

void dfs(int k, LL cur, LL cnt, int limit) {
    if(cur > N) return;
    if(!mp.count(cnt)) mp[cnt] = cur;
    else mp[cnt] = min(cur, mp[cnt]);
    for(int i = 1; i <= limit; ++i) {
        if(1.0 * cur * p[k] > N) break;
        dfs(k + 1, cur *= p[k], cnt * (i + 1), i);
    }
}

//when try to iterate, do not forget to control the bound (< ct)
void init() {
    dfs(0, 1, 1, 60);
    map<LL, LL>::iterator i = mp.begin();
    for(; i != mp.end(); ++i) rmp.insert(make_pair(i->second, i->first));
    i = rmp.begin();
    for(; i != rmp.end(); ++i) {
        if(ct > 0 && i->second < b[ct - 1]) continue;
        a[ct] = i->first;
        b[ct++] = i->second;
    }
}

int n, k, v[N];
char name[N][10];

int bit[N];

void add(int i, int x) {
    for(; i <= n; i += i & -i) bit[i] += x;
}

int find_kth(int k) {
    int m = 31 - __builtin_clz(n);
    int cnt = 0, ret = 0;
    for(int i = m; i >= 0; --i) {
        ret += 1 << i;
        if(ret >= n || cnt + bit[ret] >= k) ret -= 1 << i;
        else cnt += bit[ret];
    }
    return ret + 1;
}

int main() {
#ifdef LOCAL
    freopen("in.txt", "r", stdin);
//  freopen("out.txt","w",stdout);
#endif
    ios_base::sync_with_stdio(0);

    init();
    while(scanf("%d%d", &n, &k) == 2) {
        memset(bit, 0, sizeof bit);
        for(int i = 1; i <= n; ++i) {
            scanf("%s%d", name[i], v + i);
            add(i, 1);
        }

        int ans, idx = 0;
        while(a[idx] <= n && idx < ct) idx++;
        int cnt = a[idx - 1], Max = b[idx - 1]; //第cnt个就是最大的
        for(int i = 1; i <= cnt; ++i) {
            int mod = n - i;
            ans = find_kth(k);
            if(mod == 0) break;
            add(ans, -1);
            if(v[ans] > 0) k = (k - 1 + v[ans]) % mod;
            else k = ((k - 1 + v[ans]) % mod + mod) % mod + 1;
            if(k == 0) k = mod;
        }
        printf("%s %d\n", name[ans], Max);
    }
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值