# Ten Point Round #9 (Div.1 + Div.2) 題解
希望大家喜歡這次的比賽 ><
## pA 三階行列式 - 計算 (Determinant Calculation)
**Prepare by Colten**
照著題目給予的公式做計算後輸出即可!但是使用 C++ 的人要小心 overflow 的問題
:::spoiler 參考解答 (C++)
```cpp=
#include <bits/stdc++.h>
#define int long long
using namespace std;
signed main(void)
{
ios_base::sync_with_stdio(false);
cin.tie(0);
int a1,a2,a3,b1,b2,b3,c1,c2,c3;
cin >> a1 >> a2 >> a3;
cin >> b1 >> b2 >> b3;
cin >> c1 >> c2 >> c3;
int ans = a1 * b2 * c3 + a2 * b3 * c1 + a3 * b1 *c2;
ans -= a1 * b3 * c2;
ans -= a2 * b1 * c3;
ans -= a3 * b2 * c1;
cout << ans << "\n";
return 0;
}
```
:::
## pB 人生海海 (People Life Ocean Wild)
**Prepared by Colten**
利用迴圈針對每一個點 $i$ 去檢查是否有符合 $a[i-1] > a[i] < a[i+1]$,並利用一個變數去記錄答案。
:::spoiler 參考解答 (C++)
```cpp=
#include <bits/stdc++.h>
#define int long long
using namespace std;
int a[1005];
signed main(void)
{
ios_base::sync_with_stdio(false);
cin.tie(0);
int q;
cin >> q;
while(q--)
{
int n;
cin >> n;
for(int i=0;i<n;i++)
{
cin >> a[i];
}
int ans = 0;
for(int i=1;i<n-1;i++)
{
if( a[i-1] > a[i] && a[i] < a[i+1] )
{
ans++;
}
}
cout << ans << "\n";
}
return 0;
}
```
:::
## pC 四的倍數 (Multiples of four)
**Prepared by Colten**
$4$ 的倍數的判斷方式為,當最後兩位數為 $4$ 的倍數時,那麼此數則為 $4$ 的倍數。
那麼我們可以利用一個迴圈,並當迴圈發現 $str[i-1] + str[i]$ 轉成整數時,假如可以被 $4$ 整除,那麼代表共有 $i$ 種情況能與 $str[i]$ 做搭配。
舉例 : 假如目前的數字為 $1024$,當發現 $24$ 為 4 的倍數時,其可能的狀況有 $24,024,1024$。
在實作上,多用一個變數去紀錄答案,最後輸出。
:::spoiler 參考解答 (C++)
```cpp=
#include <bits/stdc++.h>
#define int long long
using namespace std;
signed main(void)
{
ios_base::sync_with_stdio(false);
cin.tie(0);
string a;
cin >> a;
int ans = 0;
int now = 0;
for(int i=0;i<a.size();i++)
{
if( i == 0 )
{
int u = a[i] - '0';
now = u;
if( u % 4 == 0 )
{
ans++;
}
}
else
{
int u = a[i] - '0';
now *= 10;
now += u;
if( u % 4 == 0 )
{
ans++;
}
if( now % 4 == 0 )
{
ans += i;
}
now %= 10;
}
}
cout << ans << "\n";
return 0;
}
```
:::
## pD Gunjyo 的點數分配問題 (Gunjyo's Points Allocation Problem)
**Prepared by Colten**
這題其實有對於每組資料 $O(1)$ 的作法 (數學解),但是對於這種題目更直覺的作法,我們可以對點數進行二分搜,每組時間複雜度 $O(logK)$。
:::spoiler 參考解答 (C++)
```cpp=
#include <bits/stdc++.h>
#define int long long
using namespace std;
void binary_search(int a,int b,int n)
{
int all = a + n;
int l = 0,r = n;
while( l < r )
{
int mid = ( l + r ) / 2;
int u1 = a + mid;
int u2 = b + ( n - mid );
if( u1 > u2 )
{
r = mid;
}
else
{
l = mid + 1;
}
}
cout << n - l + 1 << "\n";
}
signed main(void)
{
ios_base::sync_with_stdio(false);
cin.tie(0);
int q;
cin >> q;
while(q--)
{
int a,b,c;
cin >> a >> b >> c;
int all = a + c;
if( all <= b )
{
cout << 0 << "\n";
}
else
{
binary_search(a,b,c);
}
}
return 0;
}
```
:::
## pE 很短的問題 (A Short Problem)
**Prepared by Fysty**
假設所求為 $T$。
考慮集合$S_x=\{x,kx,k^2x,...\}$,其中 $x$ 不被 $k$ 整除。
注意到若 $n=|S_x|$,由鴿籠原理可知 $S_x$ 不可能取超過 $\lceil\frac{n}{2}\rceil$ 個元素,而取恰好$\lceil\frac{n}{2}\rceil$個元素的方法為取 $x,k^2x,k^4x,...$。
再觀察到對於任兩個不被 $k$ 整除的數 $x,y$,不管 $S_x$ 中那些數被選進 $B$,都不會影響 $S_y$ 中哪些被選,所以可推得 $$T=\sum_{k\not\mid x} \big\lceil\frac{|S_x|}{2}\big\rceil$$
但是這不好算,所以觀察所取的所有元素,可以發現恰好取所有最多能被 $k$ 整除偶數次的正整數,所以由排容原理可知 $T=k^n-k^{n-1}+k^{n-2}-...+(-1)^n$。
最後注意到 $kT+T=k^{n+1}+(-1)^n$,因此 $T=\frac{k^{n+1}+(-1)^n}{k+1}$,而這可以用快速冪和費馬小定理在 $O(\log n)$ 計算出來。
總時間複雜度 $O(t\log n)$。
:::spoiler 參考解答 (C++)
```cpp=
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll,ll> pll;
#define MottoHayaku ios::sync_with_stdio(false);cin.tie(0);
#define F first
#define S second
const int MOD=1e9+7;
ll fpow(ll a,ll b,ll m)
{
if(!b) return 1;
ll ans=fpow(a*a%m,b/2,m);
return (b%2?ans*a%m:ans);
}
int main()
{
MottoHayaku
ll t;
cin>>t;
while(t--)
{
ll n,k;
cin>>n>>k;
cout<<(fpow(k,n+1,MOD)+(n%2?-1:1)+MOD)%MOD*fpow(k+1,MOD-2,MOD)%MOD<<"\n";
}
}
```
:::
## pF 彈簧床 (Trampoline)
**Prepared by LittleCube**
### Subtask 1, 2
顯然,暴力:D
沒有任何實作細節
### Subtask 3
看一下題目,其實就是不帶修改的查詢,
如果把每個$k$都維護一次前綴和,就能$O(1)$查詢了,
但是顯然這樣做會MLE,所以可以透過只維護一部份(通常是取$\sqrt N$),
就可以在夠好的記憶體以及夠好的時間完成了。
複雜度:$O(N\sqrt N + Q\sqrt N)$
### Subtask 4, 5
接續剛剛的想法,要帶修改怎麼辦?
所以可以用兩個方式:線段樹或分塊
#### 分塊做法
可以發現每個查詢的位置$\%k$都會是一樣的餘數,
所以修改也只要對那部分的餘數做就好了,
複雜度$O(N\sqrt N + Q\sqrt N)$,不變,只是常數變大。
#### 線段樹做法
假設我維護$M$個數,這樣修改就是$M\log N$,
查詢如果$k>M$就是$\displaystyle \frac{N}{M}$,否則是$\log N$,
所以複雜度是$O(M N\log N + Q \max(\displaystyle \frac{N}{M}, \log N))$
差不多取$M =$ 一個蠻小的數字 就會過。
:::spoiler 參考解答 (C++,分塊)
```cpp=
#pragma GCC optimize("Ofast,unroll-loops")
#include <bits/stdc++.h>
#define int long long
#define fast ios::sync_with_stdio(0), cin.tie(0)
using namespace std;
int n, q, arr[200005], t, l, r, k, v, sqrtn, block[500][500][500], bi[200005];
int nxt(int i, int k)
{
int ti = bi[i];
i += (sqrtn - sqrtn % k);
if(i <= ti * sqrtn)
return i + k;
return i;
}
signed main()
{
fast;
cin >> n >> q;
for (int i = 1; i <= n; i++)
cin >> arr[i];
sqrtn = sqrt(n);
for (int i = 1, j = 1; i <= n; j++)
for (; i <= min(j * sqrtn, n); i++)
{
bi[i] = j;
for (int k = 1; k < sqrtn; k++)
block[j][k][i % k] += arr[i];
}
for (int i = 1; i <= q; i++)
{
cin >> t;
if (t == 1)
{
int ans = 0;
cin >> l >> r >> k;
int i = l;
if (bi[l] == bi[r] || k >= sqrtn)
for (int i = l; i <= r; i += k)
ans += arr[i];
else
{
for (; i <= min(r, bi[l] * sqrtn); i += k)
ans += arr[i];
for (; i <= (bi[r] - 1) * sqrtn; i = nxt(i, k))
ans += block[bi[i]][k][i % k];
for (; i <= r; i += k)
ans += arr[i];
}
cout << ans << '\n';
}
else
{
cin >> l >> v;
for (int k = 1; k < sqrtn; k++)
block[bi[l]][k][l % k] += (v - arr[l]);
arr[l] = v;
}
}
}
```
:::