校内测试总结

对前几天测试的总结

T1:

桶哥要买一些全家桶。他有a元钱,而每个桶要b元钱。他能不能买到c个桶?

代码(没什么好解释的):

#include<iostream>
#include<cstdio>
using namespace std;
int main(){
    long long a,b,c;
    scanf("%lld%lld%lld",&a,&b,&c);
    if(a>=b*c)
        printf("Yes
");
    else
        printf("No
");
    return 0;
}

然而当初只有90分,原因是无脑除法,完全忽略除数为0的情况,但如果用乘法的话,会因1018而爆int,需开long long,于是好多人只有九十分。。。

T2:

桶哥买了n个桶, 他要将这些桶送去n个人的家。他送第i个桶需要ai的时间,需要在bi之前送到。桶哥很懒,他想要尽量晚起身去送桶。问他最晚什么时候要去送桶?

桶哥在送完一个桶之后可以紧接着去送另一个桶。

当时的第一种思路:用最晚时间减去所需时长...

20分...

然后考虑到这种情况:

如图(线段起点表示最晚送出时间(b-a),线段终点表示最晚接受时间),

容易发现如果中间有一定空档,则后面的线段就无需考虑,

再次改进算法

60分(我已经不心疼AC率了)...

再次找到一种情况:

如图,显然此时的时间并不是b点减去时长2,因此前面提到过的算法全是错的

那么换一种思路:

设变量ti(设time会因与函数同名而报错)为答案,n(桶数)

将变量ti初始化为最后一个桶的最晚接受时间,

将每个桶按照最晚接受时间排序,然后倒序遍历,如果当前有未送的桶可以送,那么将ti减去该桶所用时间,剩余桶数自减

如果没有桶可以送,那么将ti自减,直到遇到可以送的桶

此算法可保证在正确数据与一定数据范围下算出正确答案

代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
int n;
struct ti{
    int a,b,c;
}t[1000005];
bool cmp(const ti &x,const ti &y){
    return x.b<y.b;
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d%d",&t[i].a,&t[i].b),t[i].c=t[i].b-t[i].a;
    sort(t+1,t+1+n,cmp);
    int time=t[n].b;
    while(n>0){
        if(time<=t[n].b){
            time-=t[n].a;
            n--;
        }
        else
            time--;
    }
    printf("%d
",time);
    return 0;
}

T3(最难的一道):

桶哥的桶没有送完,他还有n个桶。他决定把这些桶吃掉。他的每一个桶两个属性:种类ai和美味值bi。若下标为x, y, z(下标从1开始)的三个桶满足:

x < z 且 x + y = z - 2y 且 ax = az

那么它们构成一个套餐,会产生

(x + z) * (bx - bz)

的价值。问:一共会产生多少价值?

先分析题目

现有n个桶,要找出满足搭配条件的组合并按照法则计算价值

按照公式展开,即得:

x*bx+z*bx-x*bz-z*bz

可发现其实其价值与y没有任何关系,那么只要满足(z-x)%3=0就可以

那么按照一定顺序枚举就可以计算每项对应值

然而一个个枚举过于麻烦,于是可以只按照3的倍数进行枚举

然后将其分为三组,分别按照%3的余数进行考虑,分为1,2,3,三组(其中一组%3为0,但下标为0无意义),分别以它作为起点开始遍历,每次自加3,保证“3的倍数”这一性质,

又可以发现,在枚举z的过程中,也需处理前面的x,但是对于每一个z,前面的x可以以总和的形式参与计算,也就是说并无需枚举每一个x,只需将每一个z累加,在下一次循环枚举计算时,“曾经的z”作为x之和的一个元素出现,也就是每个z都将在后面的计算中当做x处理,这样保证只有与z有关的变量会导致当前值的不同,只需将x*bx以及只与x有关的变量当做一个会自动变化的常量,使其参与计算即可。

但此种枚举可能出现种类(ai)不一致的情况,对于此,只需将x的和加上角标,其角标即为种类,也就是只有相同种类的桶可被累加在同一集合进行下面运算,

另外,不要忘记取模

代码(搬了下_rqy大佬的标程):

#include <algorithm>
#include <cctype>
#include <cstdio>
#include <cstring>
int readInt() {
  int ans = 0, c, f = 1;
  while (!isdigit(c = getchar()))
    if (c == '-') f *= -1;
  do ans = ans * 10 + c - '0';
  while (isdigit(c = getchar()));     //声明:此函数用来看这个字符是不是数字,应该等价于ASCII码比较
  return ans * f;
}
//需要快读因为数据太多
const int mod = 10007;

int b[100005], a[100005];
int S[100005], Sx[100005], Sbx[100005], Sxbx[100005];

int main() {
  int n = readInt(),m = readInt();
  for(int i = 1; i <= n; i++) b[i] = readInt() % mod;
  for(int i = 1; i <= n; i++) a[i] = readInt();   //作为角标并不能模
  int ans = 0;
  for (int cc = 1; cc <= 3; ++cc) {
    // { cc, cc+3, cc+6 ... } 分一组
    memset(S, 0, sizeof(S));
    memset(Sx, 0, sizeof(Sx));
    memset(Sbx, 0, sizeof(Sbx));
    memset(Sxbx, 0, sizeof(Sxbx));
    for(int i = cc; i <= n; i += 3) {
      ans = (ans + i % mod * Sbx[a[i]] % mod) % mod;
      ans = (ans - b[i] * Sx[a[i]] % mod) % mod;
      ans = (ans + Sxbx[a[i]]) % mod;
      ans = (ans - S[a[i]] * b[i] % mod * (i % mod) % mod) % mod;
      S[a[i]] = (S[a[i]] + 1) % mod;
      Sx[a[i]] = (Sx[a[i]] + i) % mod;
      Sbx[a[i]] = (Sbx[a[i]] + b[i]) % mod;
      Sxbx[a[i]] = (Sxbx[a[i]] + i % mod * b[i] % mod) % mod;
    }
  }
  printf("%d", (ans + mod) % mod);   //处理负数情况
  return 0;
}

后面两题都是_rqy给的思路,_rqy好强呀%%%

话说这位桶哥什么心情...

原文地址:https://www.cnblogs.com/648-233/p/10946039.html