# TOI練習賽202503潛力組
### [第一題](https://zerojudge.tw/ShowProblem?problemid=q374)
#### 考點:貪心、位元運算
#### 實作方法
##### 子題一
觀察到`T=1`且`a,b`<$2^8$,直接暴力枚舉所有情況`(0~a,包含a)`,再求最大最小值
::: spoiler Python 20% code
```python=
t=int(input())
a,b=map(int, input().split())
ans1,ans2=-1,float('inf')
for i in range(a+1):
for j in range(i+1):
if (i|j)==a and (i&j)==b:
ans1=max(ans1,i-j)
ans2=min(ans2,i-j)
print(ans1,ans2)
```
:::
:::spoiler C++ 20% code
```cpp=
#include <bits/stdc++.h>
using namespace std;
int main() {
int a, b,t;
cin>>t>>a >> b;
int ans1 = -1;
int ans2 = INT_MAX; // Use INT_MAX to represent infinity
for (int i = 0; i <= a; ++i) {
for (int j = 0; j <= i; ++j) {
if ((i | j) == a && (i & j) == b) {
ans1 = max(ans1, i - j);
ans2 = min(ans2, i - j);
}
}
}
cout << ans1 << " " << ans2 << endl;
return 0;
}
```
:::
##### AC
一、貪心
觀察到題目所求為絕對值最大和最小,並給出and和or的結果。如果`and`該位元為`1`,則兩個數的位元皆為`1`,故對於答案不會有影響。
但如果`or`的位元為`1`,and位元為`0`,則
1.求最大差距:把所有符合條件的結果給其中一個數,即為答案。更仔細觀察,其實就是`a-b`。
2.求最小差距:把第一個符合條件的給其中一個,剩下的給另外一個,就相減即為答案。
例如`11 1`
`11`=>轉換成二進制 `1011`
`1`=>二進制(補前導0) `0001`
and為0且or為1 `1010`
取最大 `1010=10`
取最小 `1000-0010=8-2=6`
由於最多到$2^{30}$-1,故每次最多檢查30位,時間複雜度O($1$)。
整體時間複雜度O($T$)。
###### 註:由於不可能存在and位元為1且or位元為0,故可將條件改為互斥(XOR)成立。
:::spoiler Python AC(0.2s, 3.3MB) code
```python=
for _ in range(int(input())):
a,b=map(int, input().split())
ans,flag=0,0
a_xor_b=a^b
for i in range(30,-1,-1):
if (a_xor_b>>i)&1:
if flag==0:
ans+=(1<<(i))
flag=1
else:ans-=(1<<(i))
print(a-b,ans)
```
:::
:::spoiler C++ AC(48ms, 324KB) code 1
```cpp=
#include <bits/stdc++.h>
using namespace std;
void solve(){
int a,b;
cin>>a>>b;
cout<<a-b<<" ";
int ans=0;
bool flag=true;
int a_xor_b=a^b;
for(int i=30;i>=0;i--){
if (((a_xor_b>>i)&1)){
if (flag){ans+=(1<<i);flag=false;}
else ans-=(1<<i);
}
}
cout<<ans<<endl;
}
int main(){
int t;
cin>>t;
while(t--)solve();
}
```
:::
:::spoiler C++ AC(48ms, 332KB) code 2(使用內建函式)
```cpp=
#include <bits/stdc++.h>
using namespace std;
void solve(){
int a,b;
cin>>a>>b;
bool flag=false;
int a_xor_b=a^b,ans;
int c=__builtin_clz(a_xor_b);//計算第一高位的1前面有幾個0,傳入unsigned int
ans=(1<<(32-c-1));
ans-=a_xor_b^ans;
cout<<a-b<<" "<<ans<<endl;
}
int main(){
int t;
cin>>t;
while(t--)solve();
}
```
:::
### [第二題](https://zerojudge.tw/ShowProblem?problemid=q375)
#### 考點:滑動窗口
#### 實作方法
##### 子題一&子題二
發現子題一`n<=50`,子題二`n<=1000`直接枚舉兩個左右座標,然後檢查區間`[l,r]`是否滿足最大值為`hmax`且最小值為`hmin`。時間複雜度O($n^3$)
子題二改成枚舉左座標後,枚舉右座標時更新最大最小值,時間複雜度降到O($n^2$)
:::spoiler Python 25% code
```python=
n = int(input())
hmin, hmax = map(int, input().split())
heights = list(map(int, input().split()))
ans = 0
for i in range(n):
for j in range(i, n):
min_height = min(heights[i:j+1])
max_height = max(heights[i:j+1])
if min_height == hmin and max_height == hmax:
ans += 1
print(ans)
```
:::
:::spoiler Python 40% code
```python=
n = int(input())
hmin, hmax = map(int, input().split())
heights = list(map(int, input().split()))
ans = 0
for i in range(n):
min_height = heights[i]
max_height = heights[i]
for j in range(i, n):
min_height = min(min_height, heights[j])
max_height = max(max_height, heights[j])
if min_height == hmin and max_height == hmax:
ans += 1
print(ans)
```
:::
::: spoiler C++ 40% code
```cpp=
#include <bits/stdc++.h>
using namespace std;
int main() {
int n;
cin >> n;
int hmin, hmax;
cin >> hmin >> hmax;
vector<int> heights(n);
for (int i = 0; i < n; ++i) {
cin >> heights[i];
}
int count = 0;
// Iterate over all pairs (i, j)
for (int i = 0; i < n; ++i) {
for (int j = i; j < n; ++j) {
int min_height = *min_element(heights.begin() + i, heights.begin() + j + 1);
int max_height = *max_element(heights.begin() + i, heights.begin() + j + 1);
if (min_height == hmin && max_height == hmax) {
count++;
}
}
}
cout << count << endl;
return 0;
}
```
:::
##### AC
1.枚舉右座標,開`flag1`紀錄是否最小值==`hmin`、`flag2`紀錄是否最大值==`hmax`。
2.如果`a[i]`超過`hmax`或小於`hmin`,新的左座標設為i+1,初始左座標設為0。
3.兩個變數紀錄右座標前`hmax`和`hmin`最大出現位置。
4.檢查`flag1`和`flag2`是否成立。如果成立,代表此右座標是合法的。且合法左座標有`min(hmax,hmin)-sl+1`個,把答案加上去即可。
:::spoiler Python AC(0.2s, 16.9MB) code
```python=
n = int(input())
hmin, hmax = map(int, input().split())
nums = list(map(int, input().split())) + [hmax + 1]
ans=0
flag1, flag2, sl ,max_hmax,max_hmin=0,0,0,-1,-1
for right in range(n):
if nums[right]==hmin:flag1=1;max_hmin=right
if nums[right]==hmax:flag2=1;max_hmax=right
if nums[right]<hmin or nums[right]>hmax:
flag1, flag2, sl=0,0,right+1
if flag1&flag2:ans+=min(max_hmin,max_hmax)-sl+1
print(ans)
```
:::
:::spoiler C++ AC(0.1s, 1.1MB) code
```cpp=
#include <bits/stdc++.h>
using namespace std;
int main() {
int n;
cin >> n;
int hmin, hmax;
cin >> hmin >> hmax;
vector<int> nums(n);
for (int i = 0; i < n; ++i) {
cin >> nums[i];
}
nums.push_back(hmax + 1); // Add hmax + 1 as the last element
long long int ans = 0;
int flag1 = 0, flag2 = 0, sl = 0;
int max_hmin = -1, max_hmax = -1;
for (int right = 0; right < n; ++right) {
if (nums[right] == hmin) {
flag1 = 1;
max_hmin = right;
}
if (nums[right] == hmax) {
flag2 = 1;
max_hmax = right;
}
if (nums[right] < hmin || nums[right] > hmax) {
flag1 = flag2 = 0;
sl = right + 1;
}
if (flag1 && flag2) {
ans += min(max_hmin, max_hmax) - sl + 1;
}
}
cout << ans << endl;
return 0;
}
```
:::
### [第三題](https://zerojudge.tw/ShowProblem?problemid=q376)
#### 考點:前綴和、差分
#### 實作方法
觀察到每次輸入人數(`p`),左界(`l`),右界(`r`),代表警察只要在閉區間`[l,r]`,都可以抓到。=>對於區間的位置每個都加上`p`,再找出最大值即可。右座標只會到$10^6$,故可以直接開長度(m+2)的陣列。
##### 子題一
n<=10 暴力修改陣列,時間複雜度O(`n*m`)
###### 註:ZJ的判題邏輯是memory error就中斷跑下一筆,所以`n,m`太大會導致Python memory error,請將第10筆以後視為TLE。
:::spoiler Python 50% code
```python=
n, m = map(int, input().split())
nums = [0] * (m + 1)
for _ in range(n):
p,l,r=(map(int, input().split()))
for i in range(l, r + 1):
nums[i] += p
ans2 = max(nums)
ans1 = nums.index(ans2)
print(ans1, ans2)
```
:::
:::spoiler C++ 50% code
```cpp=
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
int n, m;
cin >> n >> m;
vector<int> nums(m + 1, 0);
for (int i = 0; i < n; ++i) {
int p, l, r;
cin >> p >> l >> r;
for (int j = l; j <= r; ++j) {
nums[j] += p;
}
}
int ans2 = *max_element(nums.begin(), nums.end());
int ans1 = find(nums.begin(), nums.end(), ans2) - nums.begin();
cout << ans1 << " " << ans2 << endl;
return 0;
}
```
:::
##### AC
由於是一直區間修改,最後查詢,故使用差分計算,降低時間複雜度。
由於本題記憶體只有64MB,Python如果直接新開陣列把差分建構成完整陣列為會NA(80%)Memory error(20%),空間複雜度$2$倍O($m$),故使用變數計算目前數字,空間複雜度O($m$)。
時間複雜度:讀入資料O($n$),差分轉前綴和O($m+2$)=O($m$),總時間複雜度O($n+m$)
:::spoiler Python 80%
```python=
from itertools import accumulate
n,m=map(int, input().split())
diff=[0]*(m+2)
for _ in range(n):
p,l,r=map(int, input().split())
diff[l]+=p
diff[r+1]-=p
nums=list(accumulate(diff))
ans2=max(nums)
ans1=nums.index(ans2)
print(ans1,ans2)
```
:::
:::spoiler Python AC(1.2s, 21.1MB) code
```python=
def get_ans(nums,m):
ans1,ans2=0,nums[0]
last=nums[0]
for i in range(1,m+2):
last+=nums[i]
if last>ans2:
ans2=last
ans1=i
return (ans1,ans2)
n,m=map(int, input().split())
diff=[0]*(m+2)
for _ in range(n):
p,l,r=map(int, input().split())
diff[l]+=p
diff[r+1]-=p
print(*get_ans(diff,m))
```
:::
:::spoiler Python AC(0.8s, 21.1MB) code(輸入優化、main)
```python=
from sys import stdin
def get_ans(nums, m):
ans1, ans2 = 0, nums[0]
last = nums[0]
for i in range(1, m + 2):
last += nums[i]
if last > ans2:
ans2 = last
ans1 = i
return ans1, ans2
def main():
n, m = map(int, stdin.readline().split())
diff = [0] * (m + 2)
for _ in range(n):
p, l, r = map(int, stdin.readline().split())
diff[l] += p
diff[r + 1] -= p
result = get_ans(diff, m)
print(*result)
main()
```
:::
:::spoiler C++ AC(0.5s, 11.8MB) code
```cpp=
#include <bits/stdc++.h>
using namespace std;
vector<long long int> accumulate(const vector<int>& nums, int m) {
vector<long long int> res(m + 2, 0);
res[0] = nums[0];
for (int i = 1; i <= m + 1; ++i) {
res[i] = res[i - 1] + nums[i];
}
return res;
}
int main() {
int n, m;
cin >> n >> m;
vector<int> diff(m + 2, 0);
for (int i = 0; i < n; ++i) {
long long int p, l, r;
cin >> p >> l >> r;
diff[l] += p;
diff[r + 1] -= p;
}
vector<long long int> nums = accumulate(diff, m);
long long ans2 = *max_element(nums.begin(), nums.end());
int ans1 = find(nums.begin(), nums.end(), ans2) - nums.begin();
cout << ans1 << " " << ans2 << endl;
return 0;
}
```
:::
:::spoiler C++ AC(0.5s, 4.1MB) code
```cpp=
#include <bits/stdc++.h>
using namespace std;
int main(){
int n,m;
cin>>n>>m;
int diff[m+2]{0};
while(n--){
int p,l,r;
cin>>p>>l>>r;
diff[l]+=p;
diff[r+1]-=p;
}
long long int prefix=diff[0],ans1=0,ans2=diff[0];
for (int i=1;i<=m+1;i++){
prefix+=diff[i];
if (prefix>ans2){ans2=prefix;ans1=i;}
}
cout<<ans1<<' '<<ans2<<endl;
}
```
:::