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;
}


京公网安备 11010502036488号