谷仓
编程社四连测
目录
目录
题目
题目描述
有一个圆形的谷仓,共有n个房间,按顺时针编号从1到n。现在有许多头奶牛,他们都有自己最喜欢的一个房间。傍晚回家时,奶牛们去找自己最喜欢的房间。如果发现被占了,他们就会按照顺时针方向找第一个空闲的房间住进去。现在请你输出最小的空闲的房间号。注意,这个答案和奶牛们回家的顺序是无关的。
2<=n<=3000000,1<=k<=10000,A,B的值在区间[0,10^9]。
输入
输入格式:
第一行两个整数n,k。
接下来有k行。每行4个整数,x,y,a,b.表示有x头奶牛喜欢f[1],f[2]……,f[y]。其中f[i]=(a*i+b)%n+1.
输出
输出最小的空闲房间号。
样例输入
10 3
3 2 2 4
2 1 0 1
1 1 1 7
样例输出
6
解析
此题其实我们可以使用暴搜的思想解决,我们首先循环一遍,将有奶牛喜欢的房间都放一头奶牛,我们以样例为例:
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
0 | 2 | 0 | 0 | 0 | 0 | 4 | 0 | 3 | 0 |
我们首先把数据读进了 a 数组里,然而我们还需要一个变量 sum ,存放后还剩的奶牛个数
i | sum | a[1] | a[2] | a[3] | a[4] | a[5] | a[6] | a[7] | a[8] | a[9] | a[10] |
---|---|---|---|---|---|---|---|---|---|---|---|
1 | 0 | 0 | 2 | 0 | 0 | 0 | 0 | 4 | 0 | 3 | 0 |
2 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 4 | 0 | 3 | 0 |
3 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 4 | 0 | 3 | 0 |
4 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 4 | 0 | 3 | 0 |
5 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 4 | 0 | 3 | 0 |
6 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 4 | 0 | 3 | 0 |
7 | 3 | 0 | 1 | 1 | 0 | 0 | 0 | 1 | 0 | 3 | 0 |
8 | 2 | 0 | 1 | 1 | 0 | 0 | 0 | 1 | 1 | 3 | 0 |
9 | 4 | 0 | 1 | 1 | 0 | 0 | 0 | 1 | 1 | 1 | 0 |
10 | 3 | 0 | 1 | 1 | 0 | 0 | 0 | 1 | 1 | 1 | 1 |
最后我们发现只剩下 3 只奶牛未能进仓,所以我们还需要二次遍历,仍然是从头开始,将剩余牛一次放入未有奶牛的谷仓(因为最后几个位置一定是被占满的,否则就不会多出奶牛,奶牛要么是前面谷仓多出的牛,它们有仓就进最后未能进谷仓的一定是多出的奶牛,其次就是最后一个谷仓的奶牛,它们从下一个位置顺时针遍历就是从 1 号元素开始)
i | sum | a[1] | a[2] | a[3] | a[4] | a[5] | a[6] | a[7] | a[8] | a[9] | a[10] |
0 | 3 | 0 | 1 | 1 | 0 | 0 | 0 | 1 | 1 | 1 | 1 |
1 | 2 | 1 | 1 | 1 | 0 | 0 | 0 | 1 | 1 | 1 | 1 |
2 | 2 | 1 | 1 | 1 | 0 | 0 | 0 | 1 | 1 | 1 | 1 |
3 | 2 | 1 | 1 | 1 | 0 | 0 | 0 | 1 | 1 | 1 | 1 |
4 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 1 | 1 | 1 | 1 |
5 | 0 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 |
6 | 0 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 |
7 | 0 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 |
8 | 0 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 |
9 | 0 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 |
10 | 0 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 |
至此,大概的算法已经明白,所以就直接看代码吧
代码(一)
#include<cstdio>
#define M 3000000 + 5
#define LL long long
#define reg register
LL sum,n,k;
LL a[M];
int main(){
scanf("%lld%lld",&n,&k);
for (reg int i = 1;i <= k; ++ i){
int x,y;
LL a1,b;
scanf("%d%d%lld%lld",&x,&y,&a1,&b);
for (reg int j = 1;j <= y; ++ j){
int s = (a1 * j + b) % n + 1;
a[s] += x;
}
}
for (reg int i = 1;i <= n; ++ i){
if (sum || a[i]){
sum += (a[i] - 1);
a[i] = 1;
}
}
int i = 1;
while (i <= n){
if (sum || a[i]){
sum += a[i] - 1;
a[i] = 1;
}
if ( ! sum && ! a[i]){
printf("%d\n",i);
return 0;
}
++ i;
}
return 0;
}
想必这里大家会有个疑惑吧
if (sum || a[i]){
sum += (a[i] - 1);
a[i] = 1;
}
其实这里很好明白,再次运用我们分类讨论的方法:
- sum != 0 && a[i] != 0,我们就可以把a[i] - 1 只奶牛放入 sum 里
- sum == 0 && a[i] != 0 ,同上
- sum != 0 && a[i] == 0,因为这里是 ,所以加一个负数就等于减去那个数的相反数,如果 a[i] == 1 的话,其实就相当于不对他就行操作
- sum == 0 && a[i] == 0,完全进不去,既然没有多余的牛,谷仓里也没有牛,我们操作干嘛呢
但是这份代码,大家尽量不要尝试,原因有下:
-
(错得有点多) -
我们再对比一下,即将给出代码的时间(一),和这份代码的时间(二)
一)
二)
代码(二)
其实就是将第一个代码中的 while 变为 for 循环,然后再用一个 for 循环找答案 (这个竟然要快一些,可能是因为 for 比 while 节时一些吧)
#include<cstdio>
#define M 3000000 + 5
#define LL long long
#define reg register
LL sum,n,k;
LL a[M];
int main(){
scanf("%lld%lld",&n,&k);
for (reg int i = 1;i <= k; ++ i){
int x,y;
LL a1,b;
scanf("%d%d%lld%lld",&x,&y,&a1,&b);
for (reg int j = 1;j <= y; ++ j){
int s = (a1 * j + b) % n + 1;
a[s] += x;
}
}
for (reg int i = 1;i <= n; ++ i){
if (sum || a[i]){
sum += (a[i] - 1);
a[i] = 1;
}
}
for (reg int i = 1;i <= n; ++ i){
if (sum || a[i]){
sum += (a[i] - 1);
a[i] = 1;
}
}
for (reg int i = 1;i <= n; ++ i){
if ( ! a[i]){
printf("%d",i);
return 0;
}
}
}