题意:给定若干根长度为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; }