A:做游戏
思路:牛牛贪心选择即可
#include <bits/stdc++.h> using namespace std; typedef long long ll; ll a, b, c, x, y, z; int main(){ while(scanf("%lld%lld%lld%lld%lld%lld", &a, &b, &c, &x, &y, &z) != EOF){ ll ans = 0; ans += min(a, y); ans += min(b, z); ans += min(c, x); printf("%lld\n", ans); } return 0; }
B:排数字
思路:由于问子串是616的最多个数,贪心地构造616,最后计算答案
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 2e5 + 5; char s[maxn]; int n; int num1, num2; int main(){ while(scanf("%d", &n) != EOF){ num1 = num2 = 0; scanf("%s", s + 1); for(int i = 1; i <= n; i++){ if(s[i] == '6') num1++; if(s[i] == '1') num2++; } num1--; printf("%d\n", max(min(num1, num2), 0)); } return 0; }
C:算概率
思路:设dp[i][j]表示前 i 道题回答出 j 题的概率
显然dp更新式子为:dp[i][j] = dp[i - 1][j] * (第 i 题未答对) + dp[i - 1][j - 1] * (第 i 题答对)
注意:此题给出的概率是模过之后的数,所以
dp[i][j] = dp[i - 1][j] * ((1 - p[i] + mod) % mod) % mod + dp[i - 1][j - 1] * p[i] % mod; dp[i][j] %= mod; 更新dp[i][0]时要注意一下,因为dp[i - 1][-1]无意义
#include <bits/stdc++.h> using namespace std; typedef long long ll; const ll mod = 1e9 + 7; const int maxn = 2e3 + 5; ll p[maxn]; ll dp[maxn][maxn]; int n; int main(){ while(scanf("%d", &n) != EOF){ memset(dp, 0, sizeof(dp)); for(int i = 1; i <= n; i++) scanf("%lld", &p[i]); dp[0][0] = 1; for(int i = 1; i <= n; i++){ dp[i][0] = dp[i - 1][0] * ((1 - p[i] + mod) % mod) % mod; for(int j = 1; j <= i; j++){ dp[i][j] = dp[i - 1][j] * ((1 - p[i] + mod) % mod) % mod + dp[i - 1][j - 1] * p[i] % mod; dp[i][j] %= mod; } } for(int i = 0; i <= n; i++){ printf("%lld%c", dp[n][i], i == n ? '\n' : ' '); } } return 0; }
D:数三角
思路:考虑到n最多才500,可以接受的复杂度,所以暴力枚举3个点,判断是否是钝角三角形。判断时,要注意三点不可以共线,用向量即可
#include <bits/stdc++.h> using namespace std; typedef long long ll; const ll mod = 1e9 + 7; const int maxn = 2e3 + 5; struct P{ ll x, y; }p[505]; int n; bool solve(int i, int j, int k){ ll x1 = p[j].x - p[i].x; ll y1 = p[j].y - p[i].y; ll x2 = p[k].x - p[i].x; ll y2 = p[k].y - p[i].y; if(x1 * x2 + y1 * y2 < 0 && x1 * y2 != x2 * y1){ return true; } return false; } int main(){ while(scanf("%d", &n) != EOF){ for(int i = 1; i <= n; i++){ scanf("%lld%lld", &p[i].x, &p[i].y); } ll ans = 0; for(int i = 1; i <= n - 2; i++){ for(int j = i + 1; j <= n - 1; j++){ for(int k = j + 1; k <= n; k++){ if(solve(i, j, k) || solve(j, i, k) || solve(k, i, j)) ans++; } } } printf("%lld\n", ans); } return 0; }
E:做计数
感受:卡这题活活把我卡了2h,心态崩溃,打表打了3000多行,然后发现由于代码太长,提交不了。幸亏我卡了2h后,开了其他题,A了2题之后心态缓和,然后再看这一题,发现就是***题。还是我太菜了
思路: ,且 。两边同时平方,
我们就可以暴力枚举
然后对于每一个平方数,我们只需要求出其因数个数即可(i * j = 平方数)
这个数论问题在寒假基础训练营第一场也有裸题
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 3e5 + 5; ll n; ll Get(ll n){ ll ans = 1; for(ll i = 2; i * i <= n; i++){ if(n % i == 0){ ll num = 1; while(n % i == 0){ num++; n /= i; } ans *= num; } } if(n > 1) ans *= 2; return ans; } int main(){ while(scanf("%lld", &n) != EOF){ ll ans = 0; for(ll i = 1; i * i <= n; i++){ ans += Get(i * i); } printf("%lld\n", ans); } return 0; }
F:拿物品
思路:刚开始看这个题目,还以为是多校原题,不过仔细想想,忘记多校那题题目是什么了。
由于两个人都是想让自己的得分,比对方的得分多得多,所以我们就把这个问题转化一下。
假设两个人现在血量均为0
并设一个物品的两个属性为,假设牛牛拿这个物品,它的血量加a, 牛可乐血量加 -b,那么问题转化为他们的血量差
那么这个问题就简化了,每一个人选择物品的原则是,尽量让自己加的血量 + 对手减的血量最大。
简单排序,选择即可
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 2e5 + 5; struct Node{ int pos; ll val; }a[maxn]; ll n; bool cmp(Node a, Node b){ return a.val > b.val; } int main(){ while(scanf("%lld", &n) != EOF){ for(int i = 1; i <= n; i++) scanf("%lld", &a[i].val); ll tmp; for(int i = 1; i <= n; i++){ scanf("%lld", &tmp); a[i].val += tmp; a[i].pos = i; } sort(a + 1, a + n + 1, cmp); for(int i = 1; i <= n; i += 2){ printf("%d ", a[i].pos); } putchar('\n'); for(int i = 2; i <= n; i += 2){ printf("%d ", a[i].pos); } putchar('\n'); } return 0; }
G:判正误
感受:这一题也卡了我好久,最后是看有1000人过了才想到用极端方法跑的。主要是一开始看到这题,想到的是以前做过的题目,好像是一个结论题,想了挺久。
思路:直接取几个质数暴力check即可,一般模数可取1e9 + 7 或者 1e9 + 11;(一般哈希函数构造的模数也可选择这个)
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 3e5 + 5; const ll mod = 1e9 + 7; const ll mod1= 1e9 + 11; ll a, b, c, d, e, f, g; ll quick_mod(ll a, ll b, ll mod){ ll sum = 1; while(b){ if(b & 1) sum = sum * a % mod; a = a * a % mod; b /= 2; } return sum; } void print(ll a, ll b, ll mod){ printf("down = %lld, up = %lld, ans = %lld\n", a, b, quick_mod(a, b, mod)); } bool check(ll a, ll b, ll c, ll d, ll e, ll f, ll g, ll mod){ a %= mod; b %= mod; c %= mod; ll t1 = quick_mod(a, d, mod); ll t2 = quick_mod(b, e, mod); ll t3 = quick_mod(c, f, mod); if((t1 + t2 + t3) % mod == (g % mod)){ return true; } return false; } int main(){ int t; scanf("%d", &t); while(t--){ scanf("%lld%lld%lld%lld%lld%lld%lld", &a, &b, &c, &d, &e, &f, &g); if(check(a, b, c, d, e, f, g, mod1) && check(a, b, c, d, e, f, g, mod)){ printf("Yes\n"); } else{ printf("No\n"); } } return 0; }
H:施魔法
感受:一眼就出思路,可是由于当时卡E和G而且H题的一些细节也没完全搞清楚,就不敢写了
思路:贪心想,能量值肯定要排序,然后从小到大排序。dp[i]表示择优取前 i 个能量值的最小cost。
dp[i] = dp[0] + a[i] - a[1]
dp[i] = dp[1] + a[i] - a[2]
dp[i] = dp[2] + a[i] - a[3]
...
dp[i] = dp[j] + a[i] - a[j + 1] (i - j == k)
我们就可以线性更新dp[] - a[]的值;
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 3e5 + 5; const ll inf = 1e18; ll dp[maxn];///dp[i]表示取完前i个最少消耗魔力 ll a[maxn]; int n, k; int main(){ scanf("%d%d", &n, &k);///注意这里不要读入文件末尾结束,会wa memset(dp, 0, sizeof(dp)); for(int i = 1; i <= n; i++) scanf("%lld", &a[i]); sort(a + 1, a + n + 1); for(int i = 1; i < k; i++) dp[i] = inf; ll tmp = dp[0] - a[1]; dp[k] = tmp + a[k]; for(int i = k + 1; i <= n; i++){ dp[i] = a[i] - a[1]; tmp = min(tmp, dp[i - k] - a[i - k + 1]); dp[i] = min(dp[i], tmp + a[i]); } printf("%lld\n", dp[n]); return 0; }
I:建通道
感受:赛后看了一会,写几个样例,发现就是水题
思路:很显然,数字相同的点,肯定是连在一起(缩点),因为其价格为0.
那我们就考虑缩点后的点集,写出它们的二进制,从低位到高位贪心,如果低位中至少有一个1,有一个0。那我们就可以让那一位为1的与0连,为0的与1连。这就是最优的贪心策略。
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 2e5 + 5; ll a[maxn]; int n, tot; int main(){ scanf("%d", &n); for(int i = 1; i <= n; i++) scanf("%lld", &a[i]); sort(a + 1, a + n + 1); tot = unique(a + 1, a + n + 1) - (a + 1); int num_1, num_0; ll ans = 0; for(int i = 0; i < 30; i++){ num_1 = num_0 = 0; for(int j = 1; j <= tot; j++){ if(a[j] & ((ll)1 << i)){ num_1++; } else{ num_0++; } } if(num_1 != 0 && num_0 != 0){ ll cost = (ll)1 << i; ans = cost * (tot - 1); break; } } printf("%lld\n", ans); return 0; }
J:求函数
感受:模板题,就是分析的时候稍微细心一点
思路:
例子:
观察可知,我们可以用线段树维护两个括号的值。具体怎么维护,观察发现,第一个括号值直接累乘即可,第二个括号可以用前一项的第二个括号值*该项第一个括号的值 + 该项第二个括号值
由
.
.
合并
由
.
.
合并
#include <bits/stdc++.h> #define ls o << 1 #define rs o << 1 | 1 using namespace std; typedef long long ll; const int maxn = 2e5 + 5; const ll mod = 1e9 + 7; struct Node{ ll f1; ll f2; }node[maxn << 2]; int n, m; ll k[maxn], b[maxn]; void up(int o){ node[o].f1 = node[ls].f1 * node[rs].f1 % mod; node[o].f2 = ((node[rs].f1 * node[ls].f2) % mod + node[rs].f2) % mod; } void build(int o, int l, int r){ if(l == r){ node[o].f1 = k[l]; node[o].f2 = b[l]; return ; } int mid = (l + r) / 2; build(ls, l, mid); build(rs, mid + 1, r); up(o); } void update(int o, int l, int r, int x, ll k, ll b){ if(l == r){ node[o].f1 = k; node[o].f2 = b; return ; } int mid = (l + r) / 2; if(mid >= x) update(ls, l, mid, x, k, b); else update(rs, mid + 1, r, x, k, b); up(o); } Node ans; void query(int o, int l, int r, int x, int y){ if(l >= x && y >= r){ ans.f1 = ans.f1 * node[o].f1 % mod; ans.f2 = (node[o].f1 * ans.f2 % mod + node[o].f2) % mod; return ; } int mid = (l + r) / 2; if(mid >= x) query(ls, l, mid, x, y); if(y > mid) query(rs, mid + 1, r, x, y); } int main(){ scanf("%d%d", &n, &m); for(int i = 1; i <= n; i++){ scanf("%lld", &k[i]); } for(int i = 1; i <= n; i++){ scanf("%lld", &b[i]); } build(1, 1, n); for(int i = 1; i <= m; i++){ int opt; scanf("%d", &opt); if(opt == 1){ int l; ll K, B; scanf("%d%lld%lld", &l, &K, &B); update(1, 1, n, l, K, B); } else{ int l, r; scanf("%d%d", &l, &r); if(r < l) swap(l, r); ans.f1 = 1; ans.f2 = 0; query(1, 1, n, l, r); printf("%lld\n", (ans.f1 + ans.f2) % mod); } } return 0; }