# 期初熱身
## [pA. 三角形](https://codeforces.com/group/fvYhunqegc/contest/317604/problem/A)
::: spoiler 想法
兩邊長的和不大於第三邊
:::
::: spoiler AC code
時間複雜度:$O(t)$
``` cpp=
#include <bits/stdc++.h>
using namespace std;
int main() {
int t;
cin >> t;
while(t--) {
int a, b, c;
cin >> a >> b >> c;
if(a < b + c && b < a + c && c < a + b) {
cout << "YES\n";
} else {
cout << "NO\n";
}
}
}
```
``` cpp=
#include <bits/stdc++.h>
using namespace std;
int main() {
int t;
cin >> t;
while(t--) {
vector<int> v(3);
for(int &in : v) cin >> in;
sort(v.begin(), v.end());
cout << (v[0] + v[1] > v[2] ? "YES" : "NO") << endl;
}
}
```
:::warning
可能的失誤
1. 沒有照輸出格式輸出(行尾要記得換行)
2. 判斷不夠完整
:::
## [pB. 區間和](https://codeforces.com/group/fvYhunqegc/contest/317604/problem/B)
:::spoiler 想法
每次詢問一個一個加起來,$a_l+a_{l+1}+...+a_{r}$
:::
:::spoiler subtask 1
``` cpp=
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 50;
int a[maxn];
int main() {
int n;
cin >> n;
for(int i = 1; i <= n; i++) cin >> a[i];
// 詢問 O(nQ)
int q;
cin >> q;
while(q--) {
int l, r;
cin >> l >> r;
int sum = 0;
for(int i = l; i <= r; i++) sum += a[i];
cout << sum << endl;
}
}
```
每次詢問時間複雜度:$O(n)$
詢問q次時間複雜度:$O(nQ)$
- subtask 1範圍
$1 ≤ n ≤ 10^4$
$1 ≤ Q ≤ 10^4$
$n \times Q=10^{8}$,一秒可以執行完。
- subtask 2範圍
$1 ≤ n ≤ 2 × 10^5$
$1 ≤ Q ≤ 2 × 10^5$
$n \times Q=10^{10}$,一秒跑步完,會超時。
:::
:::spoiler 想法2
1. 預處理前綴和,減少每次查詢需要的計算。
2. 開long long!!!
:::
:::spoiler AC code
``` cpp=
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 50;
int a[maxn];
long long prefix_sum[maxn] = {};
int main() {
int n;
cin >> n;
for(int i = 1; i <= n; i++) cin >> a[i];
for(int i = 1; i <= n; i++) prefix_sum[i] = prefix_sum[i-1] + a[i];
// 詢問
int q;
cin >> q;
while(q--) {
int l, r;
cin >> l >> r;
cout << prefix_sum[r] - prefix_sum[l-1] << endl;
}
}
```
預處理前綴和時間複雜度:$O(n)$
每次詢問時間複雜度:$O(1)$
詢問q次時間複雜度:$O(Q)$
總時間複雜度:$O(n+Q)$
:::warning
真的很多人沒有開long long,一開始我驗題也忘記了...
考慮$2 × 10^5$大小的陣列,裡面每個數字都是$10^6$,詢問全部的總和會等於$2 \times 10^{11} > 2 \times 10^9$,超出int儲存範圍。
一個小作弊的方法會是:
```cpp=
#include <bits/stdc++.h>
using namespace stdl
#define int long long
signed main() { // main回傳型別不能是long long,這邊改成signed
}
```
這樣就可以把所有int變成long long了。
:::
## [pC. 最小公倍數(LCM)](https://codeforces.com/group/fvYhunqegc/contest/317604/problem/C)
:::spoiler 想法
$a, b$的最小公倍數=$a \times b$ / ($a, b$的最大公因數)
$lcm(a, b)$=$\frac{a \times b}{gcd(a, b)}$
:::
:::spoiler AC code
### 遞迴版本
``` cpp=
#include <bits/stdc++.h>
using namespace std;
#define int long long
int gcd(int a, int b) {
if (a == 0) return b;
else return gcd(b % a, a);
}
signed main() {
int t;
cin >> t;
while(t--) {
int a, b;
cin >> a >> b;
cout << a * b / gcd(a, b) << endl;
}
}
```
### 迴圈版本
``` cpp=
#include <bits/stdc++.h>
using namespace std;
#define int long long
int gcd(int a, int b) {
while (b) {
a %= b;
swap (a, b);
}
return a;
}
signed main() {
int t;
cin >> t;
while(t--) {
int a, b;
cin >> a >> b;
cout << a * b / gcd(a, b);
}
}
```
### 內建函式版本
``` cpp=
#include <bits/stdc++.h>
using namespace std;
#define int long long
signed main() {
int t;
cin >> t;
while(t--) {
int a, b;
cin >> a >> b;
cout << a * b / __gcd(a, b);
}
}
```
:::warning
一樣記得要開long long!!!
:::
## [pD. 求求爬樓梯](https://codeforces.com/group/fvYhunqegc/contest/317604/problem/D)
:::spoiler 想法
發現關係式
$a_0=1$
$a_1=1$
$a_n=a_{n-1}+a_{n-2}$, for all $n > 1$
:::
:::spoiler subtask 1
```cpp=
#include <bits/stdc++.h>
using namespace std;
const int mod = 1e9 + 7;
int f(int x) {
if(x == 0 || x == 1) return 1;
return (f(x - 1) + f(x - 2)) % mod;
}
int main() {
int t;
cin >> t;
while(t--) {
int x;
cin >> x;
cout << f(x) << endl;
}
}
```
每次詢問時間複雜度:$O(2^n)$
總時間複雜度:$O(2^n t)$
:::
:::spoiler subtast 2
改用buttom up的方式求答案。
```cpp=
#include <bits/stdc++.h>
using namespace std;
const int mod = 1e9 + 7;
void solve() {
int n;
cin >> n;
vector<int> v(n+1, 1);
for(int i = 2; i <= n; i++) v[i] = (v[i-1]+v[i-2]) % mod;
cout << v[n] << endl;
}
signed main() {
int t;
cin >> t;
while(t--) solve();
}
```
每次詢問時間複雜度:$O(n)$
總時間複雜度:$O(n t)$
:::
:::spoiler subtast 3
### top-down 記憶化搜尋
```cpp=
#include <bits/stdc++.h>
using namespace std;
const int mod = 1e9 + 7;
const int maxn = 1e6 + 60;
vector<int> dp(maxn, -1);
int f(int x) {
if(dp[x] != -1) return dp[x];
return dp[x] = (f(x - 1) + f(x - 2)) % mod;
}
signed main() {
dp[0] = dp[1] = 1;
int t;
cin >> t;
while(t--) {
int x;
cin >> x;
cout << f(x) << endl;
}
}
```
### buttom-up 先打好表
```cpp=
#include <bits/stdc++.h>
using namespace std;
const int mod = 1e9 + 7;
const int maxn = 1e6 + 50;
vector<int> v(maxn, 1);
void init() {
for(int i = 2; i < maxn; i++) {
v[i] = (v[i-1]+v[i-2]) % mod;
}
}
signed main() {
init();
int t;
cin >> t;
while(t--) {
int in;
cin >> in;
cout << v[in] << endl;
}
}
```
建表時間複雜度:$O(n)$
查詢時間複雜度:$O(1)$
總時間複雜度:$O(n)$
:::
:::spoiler AC code
矩陣快速幂!!!
還記得$2^n$可以用分治法做到$O(logn)$嗎?
費氏數列可以轉換成這個形式:
$f(n)=f(n-1)+f(n-2)$
$f(n-1)=f(n-1)$
寫成矩陣的形式:
$\begin{bmatrix} f(n) \\ f(n-1) \end{bmatrix}=\begin{bmatrix} 1&1\\1&0 \end{bmatrix}\begin{bmatrix} f(n-1) \\ f(n-2) \end{bmatrix}$
也就是說:
$\begin{bmatrix} f(n) \\ f(n-1) \end{bmatrix}=\begin{bmatrix} 1&1\\1&0 \end{bmatrix}\begin{bmatrix} f(n-1) \\ f(n-2) \end{bmatrix}$
$\begin{bmatrix} f(n-1) \\ f(n-2) \end{bmatrix}=\begin{bmatrix} 1&1\\1&0 \end{bmatrix}\begin{bmatrix} f(n-2) \\ f(n-3) \end{bmatrix}$
$\begin{bmatrix} f(n) \\ f(n-1) \end{bmatrix}=\begin{bmatrix} 1&1\\1&0 \end{bmatrix}^{n-1}\begin{bmatrix} f(1) \\ f(0) \end{bmatrix}=\begin{bmatrix} 1&1\\1&0 \end{bmatrix}^{n-1}\begin{bmatrix} 1 \\ 1 \end{bmatrix}$
這個東西$\begin{bmatrix} 1&1\\1&0 \end{bmatrix}^{n-1}$我們就可以用快速幂的方式(也就是計算$2^n$的方式)算出來,就可以在$O(logn)$求出$f(n)$的值了。
```cpp=
// code from chshsaber(modify by rorarola)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9+7;
struct matrix{
ll arr[3][3];
void Clear() {
memset(arr, 0, sizeof(arr));
}
void init() {
Clear();
for(int i=1;i<=2;i++) {
arr[i][i] = 1;
}
}
void one() {
arr[1][1] = 1; arr[1][2] = 1;
arr[2][1] = 1; arr[2][2] = 0;
}
};
matrix pow(matrix a,matrix b) {
matrix tmp;
tmp.Clear();
for(int i=1; i<=2; i++) {
for(int j=1; j<=2; j++) {
for(int k=1; k<=2; k++) {
tmp.arr[i][j] = (tmp.arr[i][j] + a.arr[i][k] * b.arr[k][j]) % mod;
}
}
}
return tmp;
}
matrix fast_pow(int k) {
matrix tmp, A;
A.one();
tmp.init();
while(k) {
if(k & 1)
tmp = pow(tmp , A);
A = pow(A ,A);
k = (k>>1);
}
return tmp;
}
int main(){
int T;
cin>>T;
while(T--){
int k;
cin >> k;
matrix fin = fast_pow(k-1);
cout << (fin.arr[1][1] + fin.arr[1][2]) % mod << "\n";
}
}
```
:::
## [pE. 旅遊](https://codeforces.com/group/fvYhunqegc/contest/317604/problem/E)
:::spoiler 想法
從一開始的所在的城市開始進行dfs遍歷,如果邊的權重>k就不繼續向下走訪
:::
:::spoiler subtask 1
用二維陣列儲圖
:::
:::spoiler AC code
```cpp=
// code from chshsaber(modify by rorarola)
#include <bits/stdc++.h>
using namespace std;
#define pb push_back
#define X first
#define Y second
const int maxn=3e5+5;
vector<pair<int, int> > G[maxn];
bool visit[maxn];
int k,ans=0;
void dfs(int now){
for(auto v:G[now]){
if( (!visit[v.X]) && k>=v.Y){
visit[v.X]=1;
ans++;
dfs(v.X);
}
}
}
signed main(){
int n,m,x;
cin>>n>>m;
while(m--){
int a,b,c;
cin>>a>>b>>c;
G[a].pb({b,c});
G[b].pb({a,c});
}
cin>>x>>k;
visit[x]=1;
ans++;
dfs(x);
cout<<ans<<"\n";
}
```
:::warning
有些人只差一點點,比如說題目是給1-index,寫的時候沒注意到寫成0-index了。
[線上畫圖的好工具](https://csacademy.com/app/graph_editor/)
:::
## [pF. 區間Max](https://codeforces.com/group/fvYhunqegc/contest/317604/problem/F)
:::spoiler 想法
Naïve想法:
每次詢問區間最大值遍歷一次找答案
:::
:::spoiler subtask 1
```cpp=
#include <bits/stdc++.h>
using namespace std;
signed main() {
int n;
cin >> n;
vector<int> v(n+1);
for(int i = 1; i <= n; i++) cin >> v[i];
int q;
cin >> q;
while(q--) {
int cmd, l, r;
cin >> cmd;
if(cmd == 1) {
cin >> l >> r;
cout << *max_element(v.begin()+l, v.begin()+r+1) << endl;
} else {
int idx, val;
cin >> idx >> val;
v[idx] = val;
}
}
}
```
:::
:::spoiler AC code
### 根號算法
```cpp=
#include <bits/stdc++.h>
using namespace std;
const int maxn = 3e5 + 50;
constexpr int len = sqrt(maxn) + 1;
vector<int> a(maxn), b(maxn);
int max_v(int l, int r, vector<int> &v) {
return *max_element(v.begin() + l, v.begin() + r);
}
void update(int idx, int val) {
a[idx] = val;
int c_idx = idx / len;
b[c_idx] = max_v(c_idx * len, (c_idx + 1) * len, a);
}
int query(int l, int r) {
int c_l = l / len, c_r = r / len;
if (r - l + 1 <= len * 2) { // 距離比兩格還小
return max_v(l, r+1, a);
}
else {
/*
[------ c_l -----][-----c_l+1-----][][][][][-----c_r---------]
[. . . l, l+r . .][(c_l+1)*len . .][][][][][c_r*len . . r . .]
*/
int seg1 = max_v(l, (c_l + 1) * len, a);
int seg2 = max_v(c_l + 1, c_r, b);
int seg3 = max_v(c_r * len, r + 1, a);
return max({seg1, seg2, seg3});
}
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);
int n;
cin >> n;
for(int i = 1; i <= n; i++) {
cin >> a[i];
update(i, a[i]);
}
int q;
cin >> q;
while(q--) {
int cmd;
cin >> cmd;
if(cmd == 1) {
int l, r;
cin >> l >> r;
cout << query(l, r) << '\n';
} else {
int idx, val;
cin >> idx >> val;
update(idx, val);
}
}
}
```
更新時間複雜度:$O(\sqrt{n})$
查詢時間複雜度:$O(\sqrt{n})$
總時間複雜度:$O((n+t)\sqrt{n})$
### 線段樹
```cpp=
// code from chzhong
#include<bits/stdc++.h>
using namespace std;
#define IOS {ios::sync_with_stdio(false); cin.tie(0);}
#define int long long
struct S_T{
int _tree[1200005] = {};
void update(int pos, int l, int r, int idx, int val){
if(l == r) _tree[pos] = val;
else{
int m = (l + r) >> 1;
if(idx <= m) update(pos * 2, l, m, idx, val);
else update (pos * 2 + 1, m + 1, r, idx, val);
_tree[pos] = max(_tree[pos * 2], _tree[pos * 2 + 1]);
}
}
int find_max(int pos, int l, int r, int ask_l, int ask_r){
if(ask_l <= l && r <= ask_r) return _tree[pos];
int m = (l + r) >> 1, max_num = 0;
if(ask_l <= m) max_num = max(max_num, find_max(pos * 2, l, m, ask_l, ask_r));
if(m + 1 <= ask_r) max_num = max(max_num, find_max(pos * 2 + 1, m + 1, r, ask_l, ask_r));
return max_num;
}
}tree;
signed main(){
IOS;
int n, t, a, b, c, k;
cin >> n;
for(int i = 1; i <= n; i++){
cin >> k;
tree.update(1, 1, n, i, k);
}
cin >> t;
while(t--){
cin >> a >> b >> c;
if(a == 1){
cout << tree.find_max(1, 1, n, b, c) << endl;
}
else if(a == 2){
tree.update(1, 1, n, b, c);
}
}
}
```
更新時間複雜度:$O(logn)$
查詢時間複雜度:$O(logn)$
總時間複雜度:$O((n+t)logn)$
:::warning
教線段樹的時候忘記講到,線段樹的記憶體要開陣列大小的4倍大,不知道為什麼再找人(比如說我)問一下。
:::
## [pG. 疫情](https://codeforces.com/group/fvYhunqegc/contest/317604/problem/G)
:::spoiler 想法
一層一層的擴散,正好是bfs的概念。
:::
:::spoiler AC code
```cpp=
// code from chshsaber
#include <bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(0);cin.tie(0);
#define X first
#define Y second
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define All(x) x.begin() , x.end()
#define mem(aa,x) memset(aa,x,sizeof aa)
#define pb push_back
#define lower_bit(i) i&(-i)
#define int long long
typedef long long ll;
typedef pair<int,int> pii;
typedef vector<int> vi;
const int maxn=3e5+5;
vi G[maxn];
int ans[maxn];
main(){IOS
int n,m,k;
queue<int> q;
cin>>n>>m>>k;
while(m--){
int a,b;
cin>>a>>b;
G[a].pb(b);
G[b].pb(a);
}
mem(ans,-1);
while(k--){
int num;
cin>>num;
ans[num]=0;
q.push(num);
}
while(q.size()){
int now=q.front();
q.pop();
for(auto v:G[now]){
if(ans[v]==-1){
ans[v]=ans[now]+1;
q.push(v);
}
}
}
rep(i,1,n) cout<<ans[i]<<" ";
cout<<"\n";
}
```
時間複雜度:每個點被走訪到就不會再走訪第二次了,所以會是$O(n)$
## [pH. N元方程式](https://codeforces.com/group/vE9XxvRczp/contest/315272/problem/pH)
:::spoiler 想法
看到這個題目,可以聯想到上課講過的硬幣問題,忘記的再去翻一下講義呦~
:::
:::spoiler subtask 1
直接枚舉
```cpp=
// code from no_ideas
#include <iostream>
using namespace std;
int main(){
long long int t, k, tmp, count;
cin>>t;
while(t--){
count = 0;
cin>>k;
tmp = k;
for(int i=0; i<k+1; i++)
for(int j=0; j<k/13+1; j++)
for(int l=0; l<k/43+1; l++)
for(int m=0; m<k/139+1; m++)
for(int n=0; n<k/260+1; n++)
for(int o=0; o<k/1309+1; o++)
for(int p=0; p<k/2597+1; p++)
if(tmp - p*2597 - o*1309 - n*260 - m*139 - l*43 - j*13 - i == 0)
count++;
cout<<count<<endl;
}
return 0;
}
時間複雜度:這邊可以直接用手算會枚舉多少次。
```
:::
:::spoiler subtask 2
有用到動態規劃,可惜忘記打表
```cpp=
// code from bonginn
// code from bonginn
#include <iostream>
using namespace std;
int main()
{
int n;
cin >> n;
int a[7] = {1,13,43,139,260,1309,2597};
int c;
while(n--){
long long b[300005] = {0};
b[0] = 1;
cin >> c;
for(int i=0;i<7;i++)
for(int j=a[i];j<=c;j++)
b[j] += b[j-a[i]];
cout << b[c] << "\n";
}
}
```
時間複雜度:O(kt)
:::
:::spoiler AC code
```cpp=
// code from chzhong
#include<bits/stdc++.h>
using namespace std;
#define IOS {ios::sync_with_stdio(false); cin.tie(0);}
#define int long long
int x[7] = {1, 13, 43, 139, 260, 1309, 2597};
int t, h, a[8][300005] = {};
signed main(){
IOS;
for(int i = 1; i <= 7; i++){
for(int j = 1; j <= 300000; j++){
if(x[i - 1] <= j){
if(j == x[i - 1]) a[i][j] = 1;
a[i][j] += a[i][j - x[i - 1]] + a[i - 1][j];
}
else a[i][j] = a[i - 1][j];
}
}
cin >> t;
while(t--){
cin >> h;
cout << a[7][h] << endl;
}
}
```
時間複雜度:O(k+t)
:::
## [pH. N元方程式](https://codeforces.com/group/fvYhunqegc/contest/317604/problem/H)
:::spoiler 題解
參考dp,背包問題的部分。
:::
---
總結:
謝謝楊秉宇幫忙出題,他好電orz