分析:约瑟夫环的升级版,求最大糖果数(因子数可用反素数打表获得)
维护一颗线段树,区间存储剩余孩子数
这里涉及一个新的概念: 反素数
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 500000 + 10;
int antiprime[]={1,2,4,6,12,24,36,48,60,120,180,240,360,720,840,
1260,1680,2520,5040,7560,10080,15120,20160,25200,
27720,45360,50400,55440,83160,110880,166320,221760,
277200,332640,498960,554400,665280
};
int factor[]={1,2,3,4,6,8,9,10,12,16,18,20,24,30,32,36,40,48,60,
64,72,80,84,90,96,100,108,120,128,144,160,168,180,
192,200,216,224
};
int seg[maxn<<2];
int name[maxn][11], value[maxn];
void build(int root, int l, int r){
seg[root] = r-l+1; //初始化
if (l == r) return;
int m = (l+r) >> 1;
build(root<<1, l, m);
build(root<<1|1, m+1, r);
}
int update(int root, int l, int r, int key){
seg[root] --;
if (l == r) return l;
int m = (l+r) >> 1;
if (seg[root<<1] >= key) update(root<<1, l, m, key);
else update(root<<1|1, m+1, r, key-seg[root<<1]); //类似排队问题
}
int main(){
int n, k, &mod = seg[1];
while (scanf("%d %d", &n, &k) != EOF){
build(1, 1, n);
for (int i = 1; i<=n; i++){
scanf("%s%d", name[i], &value[i]);
}
int cnt = 0;
while (antiprime[cnt] <= n && cnt < 35) cnt++;
int Max = factor[cnt-1]; //根据反素数打表可求得最大因字数,可推得p值
int p = antiprime[cnt-1];
value[0] = 0;
int pos = 0;
for (int i = 0; i<p; i++){
if (value[pos] > 0){ // 顺时针
k=((k+value[pos]-2)%mod+mod)%mod+1;<span class="comment">//涉及模运算令代号从0开始,所以开始要减1,如同数空位线段树一样,k得到的是新树的第几个位置</span><span> </span>
}
else{//逆时针
k = ((k+value[pos]-1)%mod+mod)%mod+1;
}
pos = update(1, 1, n, k);
}
printf("%s %d\n", name[pos], factor[cnt-1]);
}
return 0;
}
本文介绍了一个基于约瑟夫环问题的升级版算法实现,利用线段树和反素数概念来解决最大糖果数的问题。通过预处理反素数和因子数表,结合线段树维护剩余孩子的数量,实现高效求解。
868

被折叠的 条评论
为什么被折叠?



