POJ2886 Who Gets the Most Candies?(树状数组+二分+素数)

POJ2886 Who Gets the Most Candies?(树状数组+二分+素数)

类似于约瑟夫环的一道题目。关键在于如何快速的求出下一个即将出队的孩子的编号。普通的约瑟夫环问题可以去模拟,但是数据量大不行。所以需要直接求出。

下一个即将出队的孩子为+A时,其在剩余孩子中的位置如下:
  k k = ( k + n e x t [ p o s ] − 2 ) % m o d + 1   \ kk = (k + next[pos] -2)\% mod + 1\,  kk=(k+next[pos]2)%mod+1
其中mod是此时还剩下的孩子的数量,k代表当前位置的孩子是剩下的孩子中的第k个,如果下一个出队的孩子是当前孩子左边的第A个时,直接让k加上A减去1就好了,但是为了防止余数出现0的情况,需要先减去一个1,最后再加上1。

下一个即将出队的孩子为-A时
  k k = ( ( k + n e x t [ p o s ] − 1 ) % m o d + m o d ) % m o d + 1   \ kk = ((k + next[pos] -1)\% mod + mod)\% mod + 1\,  kk=((k+next[pos]1)%mod+mod)%mod+1

这个是为了防止出现负数所以多加了个mod,其实还挺绕的。。
画个图看看。

然后我们既然知道了下一个即将出队的孩子在剩下的孩子中位置。
那么我就需要找到其具体的编号,怎么做呢,就是先用树状数组维护
BIT[i]代表前i个孩子中剩下的个数,那么我们就可以利用之前求出来的位置,去搜索具体的i,也就是如果BIT[j] == kk,那么孩子的位置一定位于位置j或者小于j。

为了更快速的找到孩子的位置,我们可以根据树状数组的单调性,利用二分查找去找。

还有就是约数的个数事先打表。

#include 
#include 
#include 
#include 
using namespace std;
const int max_n = 5e5;
int num[max_n + 10], prime[max_n + 10];
int bit[max_n + 10];
char name[max_n + 10][20];
int n, k;

inline int lowbit(int i) {return i & -i;}

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

int sum(int i) {
    int s = 0;
    while (i > 0) {
        s += bit[i];
        i -= i & -i;
    }
    return s;
}

int binary_search(int x) {
	// 二分找位置。
    int l = 1, r = n, ans;
    while (l <= r) {
        int mid = l+(r-l)/2;
        int s = sum(mid);
        if (s < x)  
            l = mid + 1;
        else {
            r = mid - 1;
            ans = mid;
        }
    }
    return ans;
}

void getPrime() {
	// 得到每个数约数的个数。
    for (int i = 1; i<= max_n; i++) {
        for (int j = i; j <= max_n; j += i) {
            prime[j]++;
        }
    }
}

int main() {
    
    scanf("%d%d", &n, &k);
    for (int i = 1; i <= n; i++) {
        scanf("%s%d", name[i], &num[i]);
        add(i, 1);
    }
    
    getPrime();
    int now = k, rem = n;
    int res = prime[1], pos = now;
    for (int i = 2; i <= n; i++) {
        add(now, -1); //对于出队的孩子的位置置成-1。
        rem--;
        int kk = num[now];
        if (kk > 0) k = (k + kk - 2)%rem + 1;
        else        k = ((k + kk - 1)%rem + rem)%rem + 1;
        now = binary_search(k); 
        if (prime[i] > res) {
            res = prime[i];
            pos = now;
        }
    }
    
    printf("%s %d\n", name[pos], res);
    
    return 0;
}


你可能感兴趣的:(POJ/CF/常用算法,算法,数据结构,cpp)