雷比较多,须要一个一个踩。node
考察点 : 签到题,是否细心(就是看你可否跳过坑) 坑点 : 注意都用 long long 数据类型(两个数相加 可能也会爆 int 哦,刚开始就 卡在这了)
Code:ios
#include <iostream> #include <algorithm> using namespace std; typedef long long LL; LL a,b,c,x,y,z; LL res = 0; int main(void) { scanf("%lld%lld%lld",&a,&b,&c); // long long scanf("%lld%lld%lld",&x,&y,&z); res += min(a,y) + min(b,z) + min(c,x); cout << res << endl; return 0; }
考察点 : 字符串,及特判,对某些名词的概念理解 坑点 : 必定要认真读题,认真读题,题中是子串,而不是子序列,子串是连续的,因此 616必定是连续的,好比 61616就是 两个,而不是三个,只统计616,不用统计61616,也不能随意组合,由于是连续的。
Code :函数
#include <map> #include <set> #include <queue> #include <deque> #include <cmath> #include <vector> #include <cstdio> #include <string> #include <cstring> #include <iostream> #include <algorithm> using namespace std; typedef long long LL; string str; int n; LL one,six; int main(void) { scanf("%d",&n); cin >> str; for(int i = 0; i < str.length(); i ++) { // 只须要判断 6 和 1 的个数便可 if(str[i] == '1') one ++; if(str[i] == '6') six ++; } six --; if(one == 0 || six < 1) cout << 0 << endl; else cout << min(six,one); // 子串是连续的,子序列能够是不连续的 return 0; }
考察点 : dp ,几率, 同余定理,模运算下的意义
坑点 : long long ,模的数通常都比较大,因此用 long long 比较保险
最好 + 上一个 MOD 防止出现 负数ui
遇到 DP 就歇菜,只能慢慢啃了。 DP 最容易的是只须要一个转移方程,最艰难的就是找到这个转移方程。(通常是题最后问的是什么咱们就设什么) 假设 DP[i][j] 表示前 i 道题中 刚好作对 j 道的几率是多少,下一步就会出现两种状况: 一、第 i 道是对的,那么 dp[i][j] = dp[i - 1][j - 1] * p[i]; 二、第 i 道是错的,那么 dp[i][j] = dp[i - 1][j] * (1 - p[i]); // 1 - p[i] 是作错的几率 因此 dp[i][j] = dp[i - 1][j - 1] * p[i] + dp[i - 1][j] * (1 - p[i]);
Code:spa
#include <cstdio> #include <string> #include <cstring> #include <iostream> #include <algorithm> using namespace std; typedef long long LL; const int MOD = 1e9 + 7; const int maxn = 2020; LL dp[maxn][maxn],p[maxn]; int n; int main(void) { scanf("%d",&n); for(int i = 1; i <= n; i ++) { scanf("%lld",&p[i]); } dp[0][0] = 1; for(int i = 1; i <= n; i ++) { // 前 i 道中 0 道正确的几率也就是没有正确的几率(初始化,为后面作准备) dp[i][0] = dp[i - 1][0] * (MOD + 1 - p[i]) % MOD; // + 1是为了防止取到负数 (同余定理) } for(int i = 1; i <= n; i ++) { for(int j = 1; j <= i; j ++) { dp[i][j] =( (dp[i - 1][j - 1] * p[i]) % MOD + dp[i - 1][j] * (MOD + 1 - p[i]) ) % MOD; // 单个模跟 + 括号 模总体是不同的 } } for(int i = 0; i <= n; i ++ ) { cout << dp[n][i] << ' '; } return 0; }
考察点 : 数学知识,动手能力 坑点 : 多画画就都看出来了,三点共线的必定没法组成三角形,须要咱们进行特判(太坑了) 平面内任意三个点均可以组成一个三角形,只有是线段是咱们才会用三角形定理去判断。
这道题都能看出来,但能作对仍是不太容易的,尤为对于像我这种渣渣来讲,就是动手能力太差,其实看到这道题就应该先想一想 有哪些地方须要特判的(三点共线卡了将近半个小时,最后仍是从别人那里听来的,太不容易了)。
Code :3d
#include <map> #include <set> #include <queue> #include <deque> #include <cmath> #include <vector> #include <cstdio> #include <string> #include <cstring> #include <iostream> #include <algorithm> using namespace std; typedef long long LL; const int maxn = 505; struct node { double x,y; } dot[maxn]; int n; double a[5]; double num(int x,int y) { return (dot[x].x - dot[y].x) * (dot[x].x - dot[y].x) + (dot[x].y - dot[y].y) * (dot[x].y - dot[y].y) ; } int main(void) { scanf("%d",&n); for(int i = 1; i <= n; i ++) { scanf("%lf%lf",&dot[i].x,&dot[i].y); } LL res = 0; for(int i = 1; i <= n; i ++) { for(int j = i + 1; j <= n; j ++) { for(int k = j + 1; k <= n; k ++) { if((dot[k].y - dot[i].y) * (dot[j].x-dot[i].x) - (dot[j].y-dot[i].y)*(dot[k].x-dot[i].x) == 0) continue; // 用斜率来判断三点是否共线 a[0] = num(i,j),a[1] = num(i,k),a[2] = num(j,k); //钝角三角形: 假设 C 是最长边,需知足 A^2 + B^2 < C^2 或者一个角 > 90度 sort(a,a + 3); if(a[0] + a[1] < a[2]) res ++; } } } printf("%lld\n",res); return 0; }
考察点 :数论,思惟
坑点 : 不一样的两个数交换位置也算一组(哈哈哈哈哈,这也能够,服了,仍是想的不够周到)code
乍一看,数论呀,直接跳过,结束后看题解,真easy。(仍是能力不济) 咱们须要将原来的式子简化一下,而后缩减范围,就像咱们以前学的,遇到根号开根号,而后观察这个式子的性质。
Code:blog
#include <map> #include <set> #include <queue> #include <deque> #include <cmath> #include <vector> #include <cstdio> #include <string> #include <cstring> #include <iostream> #include <algorithm> using namespace std; typedef long long LL; int n; vector<int>num; int main(void) { scanf("%d",&n); LL res = 0; for(int i = 1; i * i <= n; i ++) { if(i == sqrt(i * i)) { num.push_back(i * i); } } for(int i = 0; i < num.size(); i ++) { for(int j = 1; j * j <= num[i]; j ++) { if(num[i] % j == 0) { if(num[i] / j != j) res += 2; // i 和 j 位置不相等的时候互换位置也算,啊啊啊 else res ++; } } } cout << res << endl; return 0; }
考察点: 贪心(邻项交换),排序
坑点 : 错误的贪心思想,哪一个属性的值大就选哪一个,这个并非最优结果.
eg: a : 2 200
b : 1000 50
若是咱们按照上述贪心思想,先对 a 排序取最大的显然不是最优的。排序
也有多是总体相乘再排序,这样会获得一个最优得结果)(最惋惜的是我前几天刚作过一道相似得题目,可是当时没有总结 惨痛得教训,学到得知识必定要及时整理和复习)
#include <map> #include <set> #include <queue> #include <deque> #include <cmath> #include <vector> #include <cstdio> #include <string> #include <cstring> #include <iostream> #include <algorithm> using namespace std; typedef long long LL; const int maxn = 2e5 + 10; struct node { LL a,b,pos; }gooda[maxn]; int vis[maxn]; int n; bool cmp1(node a,node b) { return a.a > b.a; } int main(void) { scanf("%d",&n); for(int i = 1; i <= n; i ++) { scanf("%lld",&gooda[i].a); } for(int i = 1; i <= n; i ++) { scanf("%lld",&gooda[i].b); gooda[i].a += gooda[i].b; gooda[i].pos = i; } sort(gooda + 1,gooda + 1 + n,cmp1); vector<LL>a,b; for(int i = 1; i <= n; i ++) { if(i % 2 == 1) a.push_back(gooda[i].pos); else b.push_back(gooda[i].pos); } for(int i = 0; i < a.size(); i ++) { cout << a[i] << " "; } cout << endl; for(int i = 0; i < b.size(); i ++) { cout << b[i] << " "; } cout << endl; return 0; }
考察点 : 快速幂,取模 坑点 : 换几个模试试 MOD 1e9 + 7 有可能过不了, MOD % 1e9 + 8 就过了(哈哈哈哈,服了)
这么大次方,确定选 快速幂,又有这么大的数,得取模呀(虽然题目中没有这样要求,可是咱们能够这样尝试一下)
Code :
#include <map> #include <set> #include <queue> #include <deque> #include <cmath> #include <vector> #include <cstdio> #include <string> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int MOD = 1e9 + 8; typedef long long LL; LL a,b,c,d,e,f,g; LL quick_mod(LL a,LL b) { LL res = 1; while(b) { if(b & 1) res = res % MOD * a % MOD; a = a % MOD * a % MOD; b >>= 1; } return res; } int main(void) { int t; scanf("%d",&t); while(t --) { scanf("%lld%lld%lld%lld%lld%lld%lld",&a,&b,&c,&d,&e,&f,&g); LL ans = quick_mod(a,d) % MOD + quick_mod(b,e) % MOD + quick_mod(c,f) % MOD ; if(ans % MOD == g % MOD) { cout << "Yes" << endl; } else { cout << "No" << endl; } } return 0; }
考察点 : DP,思惟,推理 坑点 : 遇到 DP 就凉凉
Code : 待补(目前看题解还没搞透彻)
考察点 : Tree,位运算,思惟
坑点 : 能想到就完事了,去重
初看这道题,这不是最小生成树的模板吗,可是定睛一看,发现没边权呀,这不是歇菜了。 后来在一位大佬的指导下,终于将其看懂,理解,而且AC。 这道题仍是蛮有特色的 : 求 异或(二进制位下相同 取 0 不一样取 1),并且仍是 lowbit(异或)下的最小值。 咱们知道两个不一样的数在二进制下必定存在某一位是不一样的(一个是 0 ,一个是 1),那么在咱们的全部数中,也 必定存在在更低的位置(靠右的)有两个不一样的二进制位,咱们将这两个不一样的二进制位取异或,获得的必定是 1 , 再lowbit那么这个确定就是这两个点的之间的最小边权(咱们说了是在最低的位置),而后咱们知道全部数均可以 经过二进制表示出来,那么这个数的二进制位与最低位的同一个位置的数必定不是 0 就是 1 ,咱们知道最小边 权的一端存在一个 0 ,另外一端存在一个 1,那么咱们只要让这个数的的该二进制位是 0 就和 1 相连,是 1 就和 0 相连,由于异或。到这里,就都是最小边权了,最后的最小花费就是 (m - 1) * 最小边权。 lowbit(x) = x & (-x); lowbit () 取得的值 1,2,4,8........(因此最多只须要枚举30次)(能够写个数的二进制位模拟一下)
#include <map> #include <set> #include <queue> #include <deque> #include <cmath> #include <vector> #include <cstdio> #include <string> #include <cstring> #include <iostream> #include <algorithm> using namespace std; typedef long long LL; const int maxn = 2e5 + 10; int n,value; bool Exit[31][2]; set<int>sets; set<int>::iterator it; int main(void) { scanf("%d",&n); for(int i = 1; i <= n; i ++) { scanf("%d",&value); sets.insert(value); // 题中没有说不会有重复的值,须要去重,这也是一个坑点 } for(it = sets.begin(); it != sets.end(); it ++) { // 将全部数的二进制位进行标记 for(int i = 0; i <= 30; i ++) { int value = *it; if((value >> i) & 1) Exit[i][1] = true; else Exit[i][0] = true; } } LL res = 0; for(int i = 0; i <= 30; i ++ ) { if(Exit[i][0] && Exit[i][1]) { // 寻找最小的异或的位置 res = (sets.size() - 1) * (1 << i); break; } } cout << res << endl; return 0; }
J :求函数
考察点 : 线段树的基本操做
坑点 : 代码比较长,注意细节
线段树咱们确定要找到须要合并得东西,例如求区间和咱们须要父节点存储得是两个数得和,求区间最大值咱们须要 往上推送的是两个数的最大值,那这个须要往上pushup的是啥呢 ? 咱们发现 : f(L) = kL + b 因此 f(L1) = f(f(L)) = k(k * L + b) + b = k * k * L + k * b + b; 因此咱们pushup 的应该是 k * k + k * b + b (K, b 都是已知的)(能够分红两部分)
#include <cstdio> #include <string> #include <cstring> #include <iostream> #include <algorithm> #define int long long using namespace std; const int maxn = 2e5 + 10; const int MOD = 1e9 + 7; typedef long long LL; struct node { int l,r,k,b; } tree[maxn << 2]; int a[maxn],b[maxn]; int n,m; int ans,res; signed main(void) { void build(int u,int l,int r); void update(int u,int x); void query(int u,int l,int r); scanf("%lld%lld",&n,&m); for(int i = 1; i <= n; i ++) { scanf("%lld",&a[i]); } for(int i = 1; i <= n; i ++) { scanf("%lld",&b[i]); } build(1,1,n); while(m --) { int op,pos,l,r; scanf("%lld",&op); if(op == 1) { scanf("%lld",&pos); update(1,pos); } else { ans = 0,res = 1; // 这里咱们设置成全局变量,就不用最后去合并了,最好仍是合并吧,这样写总感受有点不妥 scanf("%lld%lld",&l,&r); query(1,l,r); cout << (ans + res) % MOD << endl; } } return 0; } void pushup(int u) { tree[u].k = (tree[u << 1].k * tree[u << 1 | 1].k) % MOD; tree[u].b = (tree[u << 1 | 1].k * tree[u << 1].b + tree[u << 1 | 1].b ) % MOD; return ; } void build(int u,int l,int r) { if(l == r) { tree[u].l = l; tree[u].r = r; tree[u].k = a[l]; tree[u].b = b[l]; return ; } tree[u].l = l; tree[u].r = r; int mid = l + r >> 1; build(u << 1,l,mid); build(u << 1 | 1,mid + 1,r); pushup(u); return ; } void update(int u,int x) { if(tree[u].l == tree[u].r) { scanf("%lld%lld",&tree[u].k,&tree[u].b); return ; } int mid = tree[u].l + tree[u].r >> 1; if(x <= mid) update(u << 1,x); else update(u << 1 | 1,x); pushup(u); } void query(int u,int l,int r) { if(tree[u].l >= l && tree[u].r <= r) { res = (res * tree[u].k) % MOD; ans = (ans * tree[u].k + tree[u].b) % MOD; return ; } int mid = tree[u].l + tree[u].r >> 1; if(l <= mid) query(u << 1,l,r); // 这里判断条件必定不要写反,写反可能会发生段错误,调了一个多小时,最后仍是对照着别人的才看出来 if(r > mid) query(u << 1 | 1,l,r); return ; }