文章目录
刷题之Codeforces Round #783 (Div. 2)
比赛链接:Codeforces Round #783 (Div. 2)
1668A. Direction Change
- 思路:假设不考虑题目限制的不能连续两步为同一方向,则最少步数为
m
+
n
−
2
m+n-2
m+n−2。于是不妨设
n
≤
m
n\le m
n≤m,则根据题目限制,最远可到达
(
n
,
n
)
(n,n)
(n,n) 位置,后面为了满足要求,在从
(
n
,
n
)
(n,n)
(n,n) 走到
(
n
,
m
)
(n,m)
(n,m) 的途中,不得不改变方向,需要额外多走。发现你从
(
n
,
n
)
(n,n)
(n,n) 到
(
n
,
n
+
2
)
(n,n+2)
(n,n+2) 的路线为
(
n
,
n
)
→
(
n
,
n
+
1
)
→
(
n
−
1
,
n
+
1
)
→
(
n
−
1
,
n
+
2
)
→
(
n
,
n
+
2
)
(n,n)\rightarrow (n,n+1)\rightarrow (n-1,n+1)\rightarrow (n-1,n+2)\rightarrow (n,n+2)
(n,n)→(n,n+1)→(n−1,n+1)→(n−1,n+2)→(n,n+2) 四步。于是可发现规律。
- 特判: n = 1 n=1 n=1 的情况
#include<bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define per(i, a, b) for(int i = (a); i >= (b); i--)
#define ll long long
#define db double
#define VI vector<int>
#define PII pair<int, int>
const db Pi = 3.141592653589793;
const int INF = 0x7fffffff;
const int N = 1e5 + 5;
const db eps = 1e-10;
int cas, n, m, a[N];
int main(){
// freopen("1.in","r",stdin);
cin >> cas;
while(cas--){
cin >> n >> m;
if(n > m) swap(n, m);
if(n == 1){
if(m <= 2) cout << m - 1 << endl;
else cout << -1 << endl;
}
else cout << n + m - 2 + ((m - n) / 2 * 2) << endl;
}
}
1668B. Social Distance
- 思路:显然的思路是将要求间隔相近的人放的一起最优。
- 于是将所有人按照要求间隔排序,则贪心的想法便是,相邻两个人之间按照要求人数更多的人的要求落座。
#include<bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define per(i, a, b) for(int i = (a); i >= (b); i--)
#define ll long long
#define db double
#define VI vector<int>
#define PII pair<int, int>
const db Pi = 3.141592653589793;
const int INF = 0x7fffffff;
const int N = 1e5 + 5;
const db eps = 1e-10;
int cas, n, m, a[N];
bool cmp(int a, int b){
return a > b;
}
int main(){
// freopen("1.in","r",stdin);
cin >> cas;
while(cas--){
cin >> n >> m;
rep(i, 1, n) cin >> a[i];
sort(a + 1, a + 1 + n, cmp);
ll sum = 0;
a[0] = a[1];
rep(i, 1, n) sum += a[i - 1] + 1;
puts(sum <= m ? "YES" : "NO");
}
}
1668C. Make it Increasing
- 思路:观察数据范围,显然可用
O
(
n
2
)
O(n^2)
O(n2) 的复杂度。
- 观察样例,发现必定当某个位置为 0 0 0,即不移动时,花费最少。于是可以枚举每个位置为 0 0 0
- 贪心的计算:当
n
o
w
now
now 为
0
0
0 时,
- 其右面的数字向右移动为严格单增。考虑 b [ i ] b[i] b[i],则 b [ i − 1 ] + 1 ≤ b [ i ] b[i-1]+1\le b[i] b[i−1]+1≤b[i],于是寻找第一个大于等于 b [ i − 1 ] + 1 b[i-1]+1 b[i−1]+1 的 a [ i ] a[i] a[i] 的倍数为 b [ i ] b[i] b[i]。
- 其左边的数字向左移动为严格单减,则可以理解为向左移动为相反数的严格单增。于是与向右同样计算即可。
- 最后记录最小值。
#include<bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define per(i, a, b) for(int i = (a); i >= (b); i--)
#define ll long long
#define db double
#define VI vector<int>
#define PII pair<int, int>
const db Pi = 3.141592653589793;
const ll LLINF = 0x7fffffffffffffff;
const int N = 1e5 + 5;
const db eps = 1e-10;
ll n, a[N], b[N], cnt, ans;
int main(){
// freopen("1.in","r",stdin);
cin >> n;
rep(i, 1, n) cin >> a[i];
ans = LLINF;
rep(now, 1, n){
b[now] = 0, cnt = 0;
rep(i, now + 1, n){
ll tmp = b[i - 1] + 1;
ll add = tmp / a[i] + (tmp % a[i] ? 1 : 0);
b[i] = add * a[i];
cnt += add;
}
per(i, now - 1, 1){
ll tmp = b[i + 1] + 1;
ll add = tmp / a[i] + (tmp % a[i] ? 1 : 0);
b[i] = add * a[i];
cnt += add;
}
ans = min(ans, cnt);
}
cout << ans << endl;
}
1688D. Optimal Partition
-
算法:树状数组
-
思路:记 d p [ i ] dp[i] dp[i] 表示前 i i i 个数字可获得的最大答案。记 v i j ( i ≤ j ) v_{ij}(i\le j) vij(i≤j) 表示 a i + 1 a_{i+1} ai+1 到 a j a_j aj 的 s s s 值。记 s u m i = ∑ k = 1 i a [ k ] sum_i=\sum\limits_{k=1}^i a[k] sumi=k=1∑ia[k]
-
若运用复杂度为 O ( n 2 ) O(n^2) O(n2) 的方法,很容易想到利用前缀和计算出 ∑ k = i + 1 j a [ k ] \sum\limits_{k=i+1}^j a[k] k=i+1∑ja[k]。通过和 0 0 0 比较得到 v i j v_{ij} vij。再通过判断 v i j v_{ij} vij 利用 d p [ i ] dp[i] dp[i] 来更新 d p [ j ] dp[j] dp[j],更新方法为
d p [ j ] = max ( d p [ i ] + v i j ) , 1 ≤ i ≤ j − 1 dp[j]=\max(dp[i]+v_{ij}),\quad 1\le i\le j-1 dp[j]=max(dp[i]+vij),1≤i≤j−1
接着,我们发现若 s u m i < s u m j sum_{i}<sum_j sumi<sumj,则有 v i j = j − ( i + 1 ) + 1 = j − i v_{ij}=j-(i+1)+1=j-i vij=j−(i+1)+1=j−i,即 d p [ j ] = d p [ i ] + j − i dp[j]=dp[i]+j-i dp[j]=dp[i]+j−i,变形为
d p [ j ] − j = d p [ i ] − i \color{red}dp[j]-j=dp[i]-i dp[j]−j=dp[i]−i
这时,我们便可以利用树状数组存储 d p [ i ] − i dp[i]-i dp[i]−i 来在 1 ≤ i ≤ j − 1 1\le i\le j-1 1≤i≤j−1 中 O ( log n ) O(\log n) O(logn) 计算 d p [ j ] dp[j] dp[j]。 -
由于我们要存储 v i j v_{ij} vij 级别的数字,这 1 e 9 1e9 1e9 的数量级不好存储。因此我们将前缀和离散化变为 p o s [ i ] pos[i] pos[i],是和 n n n 相同的 5 e 5 5e5 5e5 级别的数量来存储。由于存储的是 d p [ i ] − i dp[i]-i dp[i]−i,因此我们每次计算 d p [ j ] dp[j] dp[j] 时都要计算 1 ≤ i ≤ j − 1 1\le i\le j-1 1≤i≤j−1 中形成的答案,即 i + s u m ( p o s [ j ] − 1 ) i+sum(pos[j]-1) i+sum(pos[j]−1)
-
#include<bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define per(i, a, b) for(int i = (a); i >= (b); i--)
#define ll long long
#define db double
#define VI vector<int>
#define PII pair<int, int>
#define lowbit(x) x & -x
const db Pi = 3.141592653589793;
const int INF = 0x7fffffff;
const int N = 5e5 + 5;
const db eps = 1e-10;
int cas, n, m;
ll a[N];
ll sum[N], tmpsum[N];
int pos[N], dp[N];
struct Fenwick{
const int n;
vector<int> h;
Fenwick(int n) : n(n), h(n + 5, -INF) {}
void add(int x, int val){
for(int i = x; i <= n; i += lowbit(i)){
h[i] = max(h[i], val);
}
}
int sum(int x){
int ans = -INF;
for(int i = x; i >= 1; i -= lowbit(i)){
ans = max(ans, h[i]);
}
return ans;
}
};
int main(){
ios::sync_with_stdio(false); cin.tie(0);
cin >> cas;
while(cas--){
cin >> n;
tmpsum[0] = sum[0] = 0;
rep(i, 1, n){
cin >> a[i];
sum[i] = sum[i - 1] + a[i];
tmpsum[i] = sum[i];
}
//前缀和离散化(因为前缀和数字太大,会使树状数组所开空间太多,装不下)
sort(tmpsum, tmpsum + 1 + n); //从 0 到 n 排序
rep(i, 0, n){
pos[i] = lower_bound(tmpsum, tmpsum + 1 + n, sum[i]) - tmpsum + 1;
}
//初始化树状数组
Fenwick fen(n + 1);
dp[0] = 0;
fen.add(pos[0], dp[0] - 0);
rep(i, 1, n){
dp[i] = dp[i - 1] + (a[i] > 0 ? 1 : (a[i] < 0 ? -1 : 0)); //初始当前dp[i]
dp[i] = max(dp[i], i + fen.sum(pos[i] - 1)); //计算当前dp[i]
fen.add(pos[i], dp[i] - i); //更新
}
cout << dp[n] << endl;
}
}
/*
5
3
1 2 -3
4
0 -2 3 -4
5
-1 -2 3 -1 -1
6
-1 2 -3 4 -5 6
7
1 -1 -1 1 -1 -1 1
*/
1688E. Half Queen Cover
- 类型:构造题
- 思路:图中红色位置放置皇后。首先左上角放置可控制黄色区域,接着右下角放置控制最后一行和一列。最后填补中间位置控制蓝色区域。根据模
3
3
3 考虑,
- 当 n = 3 x + 2 n=3x+2 n=3x+2 时, a n s = 2 x + 1 ans=2x+1 ans=2x+1。此时不需要右下角绿色区域,我们在左上斜着放 x + 1 x+1 x+1 个,右下斜着放 x x x 个可全部控制。
- 当 n = 3 x + 3 / 3 x + 1 n=3x+3/3x+1 n=3x+3/3x+1 时,额外多出的 1 o r 2 1\;or\;2 1or2 行可直接在右下角落填充 1 o r 2 1\;or\;2 1or2 个方块。于是可删除这 1 o r 2 1\;or\;2 1or2 行和列,再根据上面的方法处理 n = 3 x + 2 n=3x+2 n=3x+2 的情况(最好根据代码找找规律)
#include<bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define per(i, a, b) for(int i = (a); i >= (b); i--)
#define ll long long
#define db double
#define VI vector<int>
#define PII pair<int, int>
const db Pi = 3.141592653589793;
const int INF = 0x7fffffff;
const int N = 1e5 + 5;
const db eps = 1e-10;
int n, ans;
int main(){
// freopen("1.in","r",stdin);
cin >> n;
ans = n / 3 * 2 + (n % 3 ? 1 : 0);
cout << ans << endl;
if(ans == 1){
cout << "1 1" << endl;
return 0;
}
int k = n / 3;
if(n % 3 != 2) k--;
while(n % 3 != 2){
cout << n << " " << n << endl;
n--;
}
//k+1个
rep(i, 1, k + 1) cout << i << " " << k + 2 - i << endl;
//k个
rep(i, 1, k) cout << n + 1 - i << " " << n - k + i << endl;
}
/*
8
*/