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;
}
题意:
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;
}
相关文章推荐
- 数据结构的基础-单向链表所构造的栈 c++实现
- unity, 由Matrix4x4提取Quaternion和Vector3 及 由Quaternion,Vector3构造Matrix4x4
- U盘结构、存储原理和发明人
- Eclipse EMT Papyrus建模和MoDisco反向工程
- 排序:快速排序与选择排序
- history命令保存的最大数?
- 眼睛打架了,希望兄弟们今晚压测顺利
- 反素数学习
- sgu 259
- github新建仓库并上传
- sgu 259 分类: sgu 2015-06-17 01:50 19人阅读 评论(0) 收藏
- sgu 208
- sgu 208 分类: sgu templates 2015-06-17 01:40 24人阅读 评论(0) 收藏
- use jQuery in chrome console
- 友盟登录的accesstoken返回找不到的问题
- 我的Java开发学习之旅------>求字符串中出现次数最多的字符串以及出现的次数
- 一个从源代码里提取中文字符串的java类
- 编写高质量iOS代码
- KindEditor编辑器在ASP.NET中的使用
- JS数组方法总结