牛客算法竞赛入门课第一节习题-糖糖别胡说,我真的不是签到题目-前缀和
链接:https://ac.nowcoder.com/acm/problem/14583
来源:牛客网
糖糖别胡说,我真的不是签到题目
时间限制:C/C++ 2秒,其他语言4秒
空间限制:C/C++ 131072K,其他语言262144K
64bit IO Format: %lld
空间限制:C/C++ 131072K,其他语言262144K
64bit IO Format: %lld
题目描述
从前,有n只萌萌的糖糖,他们分成了两组一起玩游戏。他们会排成一排,第i只糖糖会随机得到一个能力值bi。从第i秒的时候,第i只糖糖就可以消灭掉所有排在他前面的和他不是同一组的且能力值小于他的糖糖。
为了使游戏更加有趣,糖糖的爸爸,娇姐,会发功m次,第i次发功的时间为ci,则在第ci秒结束后,b1,b2,.....,bci都会增加1.
现在,娇姐想知道在第n秒后,会有多少只糖糖存活下来。
输入描述:
第一行只有一个整数T(T<6),表示测试数据的组数。
第二行有两个整数n,m。表示糖糖的个数以及娇姐发功的次数。(1<=n<=50000,1<=bi<=1000000)
第三行到n+2行,每行有两个整数ai,bi,表示第i只糖糖属于那一组以及他的能力值。(0<=ai<=1,1<=bi<=1000000)
第n+3行到第n+m+2行,每行有一个整数ci,表示GTW第i次发功的时间.(1<=ci<=n)
输出描述:
总共T行,第i行表示第i组数据中,糖糖存活的个数。
示例1
输入
1 4 3 0 3 1 2 0 3 1 1 1 3 4
输出
3
参考:https://ac.nowcoder.com/acm/problem/blogs/14583
这道题如果单纯用模拟的方法肯定是要超时的,所以我们不妨换个思路,不考虑每一秒时有多少个糖糖没了,考虑每一个糖糖会不会没了。
但是,怎么考虑呢?当第i个糖糖没了,肯定是被后面的糖糖灭了的,我们假设某个j可以灭了i,那么首先j和i不同类,且满足一定的条件,也就是某时候value[j] > value[i],但是怎么可能知道呢?当加魔法的时间在到i之前时,对i,j都没有影响,不管;当从i到j-1时,i每次都会被upup,但j并不会改变,当到达j或大于时,ij一起up,差值都不变。所以会改变i和j的情况只能是在i~j-1时,且在这段时间,ij的差值逐渐减小,所以我们不妨取最小的ij差,如果仍然为正,那么就说明i会被j灭掉,我们用csum做施法的次数前缀和(表示第i秒后一共进行了几次操作),那么从i到j-1的操作数可以写成csum[j - 1] - csum[i - 1](第j-1秒后进行的操作数减去第i-1秒后进行的操作数,就得到了从i~j-1进行的操作,而这些操作对i都是有效的)因此我们可以写成v[i] + csum[j - 1] - csum[i - 1] < v[j]时,第i个糖糖会被吃掉,移项得v[i] - csum[i - 1] < v[j] - csum[j - 1],当存在j与i不同类且满足此关系式时i即会被消灭,我们不妨直接找到i往后不同类的v[k] - csum[k]最大值,每次比较,如果小于最大就被灭了。注意最大一开始都要为负无穷,否则,差值为负数不会被记上。最大值记得要更新,且是倒序遍历,因为你要求i后面的所有不同类的v[k] - csum[k]的最大值。
AC代码:
#include <bits/stdc++.h> using namespace std; const int INF = 0x3f3f3f3f; vector<pair<int, int> > v;//队列 int csum[50005];//前i秒进行了多少次操作 int max0, max1;//max0:0类v[i] - csum[i]最大 int main() { int t, ans; int n, m; int group, value; scanf("%d", &t); for(int cnt = 0; cnt < t; cnt++) { ans = 0; max1 = max0 = -INF; memset(csum, 0, sizeof(csum)); v.clear(); v.push_back(make_pair(0, 0));//我们从1到n,所以v[0]直接填充一个无意义的值无所谓。 scanf("%d %d", &n, &m); for(int i = 0; i < n; i++) { scanf("%d %d", &group, &value); v.push_back(make_pair(group,value)); } for(int i = 0; i < m; i++) { scanf("%d", &value); csum[value]++;//第i秒比之前多了一次操作 } for(int i = 1; i <= n; i++) csum[i] += csum[i - 1]; for(int i = n; i > 0; i--) { if(v[i].first)//属于1类 { if(v[i].second - csum[i - 1] < max0)//比0类的要小则被消灭 ans++; max1 = max(max1, v[i].second - csum[i - 1]);//更新1类的最大值 } else { if(v[i].second - csum[i - 1] < max1) ans++; max0 = max(max0, v[i].second - csum[i - 1]); } } printf("%d\n", n - ans); } return 0; }