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; }

浙公网安备 33010602011771号