校内测试总结
对前几天测试的总结
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\n"); else printf("No\n"); 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\n",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好强呀%%%
话说这位桶哥什么心情...