题目大意为:一个学生公寓中有多个房间,每一个房间有门牌号。已知一楼有且仅有两个房间,其门牌号分别为 1 和 2。给定数字 \(x\) ,以后每层楼中有 \(x\) 间房间。问:给你门牌号 \(n\) 和数字 \(x\) 。给出所在楼层。c++
1 <= n, x <= 1000
数组
*800
ide
很是简单,分状况讨论:this
void solve(){ int n = read(), x = read(); if (n <= 2) cout << 1 << '\n'; else cout << (n - 2 + x - 1) / x + 1 << '\n'; }
用 \(n\) 个 \(2 \times 2\) 的正方形尝试拼接出一个 \(m\times m\) 的关于主对角线对称矩阵。spa
若是能够输出:
YES
不然输出:NO
code
*900
blog
题目看起来有点麻烦,阅读起来也比较长,实际上仔细一想,只要边长能 mod 2,则咱们运用贪心的思路考虑问题,只须要一个 \(2\times 2\) 的主对角线对称矩阵便可。继承
主要的思路方向是大小类比,转大为小。游戏
void solve(){ int n = read(), m = read(); int have = 0; for (int i = 0; i < n; ++ i){ int ul = read(), ur = read(); int dl = read(), dr = read(); if (dl == ur) have = 1; } if (m % 2 != 0 || have == 0) wprint("NO"); else wprint("YES"); }
给你一个数组 \(a\) ,最开始只包含一个数字 \(1\) 。你能够作以下两种操做:ci
- 给 \(a\) 中的某一元素加一
- 复制 \(a\) 中某一元素到数组末尾
回答,须要最少操做多少步,能使数组 \(a\) 中的总和大于 \(n\)。
1 <= n <= 1e9
*1100
通过思考,显然倍增的效率是大于自增的,因而咱们能够初步得出结论,咱们须要先自增而后再倍增。
设咱们自增到 \(k\) 再进行倍增。则须要的步骤数为:
处理一下变为:
由基本不等式可得:当 \(k = \sqrt{n}\) 时存在最小值,又由于这里皆为整数,经过计算机验证以后获得以下结论:
能够记住如上结论。
void solve(){ int n = read(); int ans(0); int x = sqrt(n); ans = (n + x - 1) / x + x - 2; cout << ans << '\n'; }
给定一个长度为 \(n\) 且不含 \(0\) 的序列 \(a\),能够插入任何数字(能够为 \(\infty\) )。请问最少插入多少个数字才能保证序列中任意连续子序列和不为 \(0\) 。
2 <= n <= 200000
-1e9 <= ai <= 1e9
*1500
这题关键点在于 快速肯定区间和为 0 的状况。
由于作过相似的,因此能够很快的写出来:即用 hashmap
。
接下来复制我在CF回复别人的部分:
Let us define \(presum_{k}\) as the prefix sum, \(a_k\) as the array.
Let's define \(i,j\) as indexs(\(i < j\)):
In order to avoid this situation, we insert a \(+\infty\) behind the \(a_j\). Like this: \(a_{i+1}, \cdots, a_{j-1}, \stackrel{\infty}{\downarrow},a_{j}\).Then reconsider from \(a_j\)
void solve(){ int n = read(); unordered_map<LL, int> mp; // 0 --> not in, 1 --> in mp.clear(); LL sum(0), ans(0); for (int i = 0; i < n; ++ i){ int f = read(); sum += f; if (sum == 0 || mp.count(sum)) { ++ ans; mp.clear(); // Simulate inserting an infinite number sum = f; mp[sum] = 1; } else mp[sum] = 1; } cout << ans << '\n'; }
Alice和Bob进行剪刀石头布的游戏,总共进行\(n\)局。
Alice出石头\(a_1\)次,出剪刀\(a_2\)次,出布\(a_3\)次。
Bob出石头\(b_1\)次,出剪刀\(b_2\)次,出布\(b_3\)次。
问Alice最多赢多少次,最少赢多少次。
*1800
最大值很是简单,咱们但愿尽量能赢
实际上这题应该有更加数学的思路,可是考虑到整个问题空间的大小,所以能够经过暴力搜索直接解决。
面对样本空间很小的题目,咱们能够优先计算穷举的复杂度,快速解决。
时间复杂度大体为 \(\mathcal{O}(2\cdot 3!)\)
int a[5], b[5]; int mn; void dfs(int ca, int cb, vector<int> visa, vector<int> visb, int cnt, vector<int> fa, vector<int> fb){ visa[ca] = visb[cb] = 1; if (fa[ca] >= fb[cb]){ if ((ca + 1) % 3 == cb) cnt += fb[cb]; fa[ca] -= fb[cb]; int ok =0; for (int i = 0; i < 3; ++ i){ if (visb[i] == 0) { // 谁没有了,就在他可选的中间挑选一个 dfs(ca, i, visa, visb, cnt, fa, fb); ok = 1; } } if (ok == 0){ // 没有什么能够选的了,说明结束了 mn = min(mn, cnt); return; } } else { if ((ca + 1) % 3 == cb) cnt += fb[cb]; fb[cb] -= fa[ca]; int ok = 0; for (int i = 0; i < 3; ++ i){ if (visa[i] == 0) { dfs(i, cb, visa, visb, cnt, fa, fb); ok = 1; } } if (ok == 0){ mn = min(mn, cnt); return; } } } void solve(){ int n = read(); for (int i = 0; i < 3; ++ i) a[i] = read(); for (int i = 0; i < 3; ++ i) b[i] = read(); int mx(0); for (int i = 0; i < 3; ++ i) mx += min(a[i], b[(i + 1) % 3]); VI visa(3, 0), visb(3,0); // visa --> a是否访问, visb --> b是否访问 VI fa(3, 0), fb(3, 0); // fa --> a 的各个数个数, fb --> 同 a mn = inf; for (int i = 0; i < 3; ++ i) fa[i] = a[i], fb[i] = b[i]; for (int i = 0; i < 3; ++ i){ // 枚举各类状况 for (int j = 0; j < 3; ++ j) dfs(i, j, visa, visb, 0, fa, fb); } wprint(mn, mx); }
给定一个长为 \(n\) 含有
abc?
的字符串,?
多是abc
中的任意一个,求全部可能的无?
字符串中,子序列abc
出现的次数.结果须要 mod \(1e9 + 7\)
3 ≤ n ≤ 200000
*2000
dp
经过数据大小和题目所问的,咱们能够很快得出大体思路:时间复杂度为 \(\mathcal{O}(n)\) 的线性dp。
假如是 dp ,咱们确定须要记录一些东西才能保证在 \(\mathcal{O(1)}\) 的时间内进行状态转移,而且须要考虑记录什么和 ?
如何进行处理。
因为咱们只须要考虑 abc
,所以咱们能够只记录:
a
的数量 \(f[0]\)ab
的数量 \(f[1]\)abc
的数量 \(f[2]\)当没有遇到 ?
时转移方程很是简单,这里就不作赘述。
下面考虑当须要疑问号的状况便可:
首先,咱们清楚疑问号能够变为 a,b,c
任意一种的,用生物学的知识去想有点像分裂的过程:
当碰到 ?
号时,会在原有的基础上出现 \(3\) 种新状况。
而他们各自又继承了 cur的全部值。所以:
这对了吗?,实际上这样写表明你没有理解其核心。须要知道的最开始只拥有 \(1\) 种状况,但每遇到一个 ?
会分裂出 \(3\) 种状况,不妨假设 ?
的个数为 \(c_q\) 。则当前存在 \(3^{c_q}\) 种状况,因而你每次增长都须要考虑全部的状况。而上述方程中:a -> ab
和 ab -> abc
的转移自带鲁棒性已经考虑了这个问题,而 null -> a
的过程则没有考虑。
所以,咱们须要记录当前空间中存在状况的总个数 base
,当遇到 ?
时更新 base
的值。
作这种 \(\mathcal{O}(1)\) 的计数 dp ,确定是须要记录某些数据,若须要计数的为一个具备顺序的序列,不妨前缀考虑每一个子段的个数,逐个转移。
const int maxn = 5; LL f[maxn]; void solve(){ int n = read(); mst(f, 0); string ss; cin >> ss; LL base = 1; for (int i = 1; i <= n; ++ i){ char ch = ss[i - 1]; if (ch == 'a'){ f[0] = (f[0] + base) % MOD; }else if (ch == 'b'){ f[1] = (f[1] + f[0]) % MOD; }else if (ch == 'c'){ f[2] = (f[2] + f[1]) % MOD; }else { f[2] = (3 * f[2] % MOD + f[1]) % MOD; f[1] = (3 * f[1] % MOD + f[0]) % MOD; f[0] = (3 * f[0] % MOD + base) % MOD; base = (1LL * base * 3) % MOD; } // print(f, 3); } cout << f[2] << '\n'; }
UPD:
小号变大号太真实了: