题目链接 :http://codeforces.com/contest/831/problem/C
题意 :选手有一个初始积分,接下来有k个裁判为他加分或减分(时间顺序给出),然后告诉你n(1<=n<=k)个积分减分过程中的积分(不一定是时间顺序),问根据这些信息,他的初始积分有多少种可能
分析 : 无论存在多少种,先明确一点,只要存在那么这n个分数肯定是能够找到自己的位置的(也就是每个分数是在哪个裁判给分之后的值)。那么我们就可以找其中一个出来,将其放在各个裁判判分之后的位置,有k个裁判,那就拥有k个位置,然后根据裁判给出的分数,我们能得到一个初始值,然后根据这个初始值和各个裁判的分数,就能知道其他的分数是否有合法的位置给它。实现的时候,由于在枚举每一个位置的时候需要知道前面裁判的给分总和,如果每一次都去计算,那就很浪费时间了,可以使用一个数组来保存裁判的分数,构成前缀和数组来辅助计算。同样再考虑一个问题,就是如果选出来的那一个记忆值放在哪个裁判后面都可以的话总共就是k种了吗?其实这里有一种情况,就是如果有裁判给出0分,那就会有至少一组相邻相同的前缀和,如果其中一个位置可行,那在相同的前缀和之下另一个也必定可行,可是得到的初始值是一样的,所以需要去重处理,可以将前缀和数组进行排序和然后使用unique去重。
#include<bits/stdc++.h> using namespace std; const int maxn = 2000 + 5; int Sum[maxn], fir[maxn], sec[maxn]; int main(void) { int n, k; scanf("%d %d", &n, &k); for(int i=0; i<n; i++){ scanf("%d", &fir[i]); Sum[i+1] = Sum[i] + fir[i];///构造前缀和数组 } sort(Sum+1, Sum+1+n); for(int i=0; i<k; i++) scanf("%d", &sec[i]); int m = unique(Sum+1, Sum+1+n) - (Sum+1); int ans = m;///去重后的元素个数作为答案基数,表示可以得到的最多种类数 for(int i=0; i<m; i++){ int Init_val = sec[0] - Sum[i+1];///得到初始值 for(int j=0; j<k; j++){ if(!binary_search(Sum+1, Sum+1+n, sec[j]-Init_val)){ ans--; break; } } } printf("%d ", ans); return 0; }
瞎 :
①unique(arr, arr+n)实际进行的是伪去重,但是我们可以得到去掉重复元素之后最后一个有效元素的地址,因此去重之后的元素个数就是 unique(arr, arr+n) - arr,注意先排序。
②当频繁要进行有序的前缀求和计算或者需要对于各个前缀和进行特殊处理,要考虑提前进行处理然后存在数组中构成前缀和数组。
③暴力时候在有查找操作的时候,需要灵敏一点去想是否符合二分特性,加快计算。