HDU-4476 Cut the rope 树状数组

题意:给定若干根长度为Li的绳子,现在每条绳子只能够切成两份或者不切,问最后最多产生多少根长度相等的绳子?

解法:首先明确一定就是每条绳子最多对结果贡献2,因为一条绳子最多切成两份,且当最后结果为其长度的一半时才成立,其余绳子要么贡献为0(长度小于枚举长度),要么贡献为1(长度大于枚举长度不等于2倍的枚举长度)。因此可以枚举这个所切的长度,很容易推出这个长度一定会是某个绳长的一半,由于绳子的长度可能会出现奇数,因此给所有的长度乘以2之后再枚举每条绳子的一半就可以了,使用树状数组初始化前缀和,如果枚举的长度为Lx最后的结果为长度为Lx到MaxL的数量加上长度为2*Lx的绳子数。

代码如下:

#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <set>
using namespace std;

const int MaxN = 200000;
int N;
int bit[MaxN+5];

inline int lwb(int x) {
    return x & -x;
}

void add(int x, int val) {
    for (int i = x; i <= MaxN; i += lwb(i)) {
        bit[i] += val;    
    }
}

int sum(int x) {
    int ret = 0;
    for (int i = x; i > 0; i -= lwb(i)) {
        ret += bit[i];
    }
    return ret;
}

int cal(int x) {
    if (x <= MaxN/2) {
        int k = x << 1;
        return sum(k) - sum(k-1) + sum(MaxN) - sum(x - 1);
    } else {
        return sum(MaxN) - sum(x - 1);
    }
}

char vis[100005];
int que[100005];
int tail;

int main() {
    int T, x;
    scanf("%d", &T);
    while (T--) {
        scanf("%d", &N);
        tail = 0;
        memset(bit, 0, sizeof (bit));
        memset(vis, 0, sizeof (vis));
        for (int i = 0; i < N; ++i)    {
            scanf("%d", &x);
            add(x<<1, 1);
            if (!vis[x]) {
                vis[x] = 1;
                que[tail++] = x;
            }
        }
        int Max = 0;
        for (int i = 0; i < tail; ++i) {
            Max = max(Max, cal(que[i]));
        }
        printf("%d\n", Max);
    }
    return 0;    
}

 

posted @ 2013-05-31 18:08  沐阳  阅读(365)  评论(0编辑  收藏  举报