您的位置:首页 > 其它

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

2015-06-17 02:19 465 查看
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
;

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
;

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
;
char name
[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
;
char name
[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
;
char name
[10];

int bit
;

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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  动态求第k大