Codeforces Round 890 (Div. 2)
A. Tales of a Sort
题意:
有一个数组,每次操作可以让所有数减一,为零则不变,问最少多少次操作可使数组变有序
Idea:
答案即为 m a x ( a i ∣ i ∈ [ 1 , n − 1 ] , a i > a i + 1 ) max(a_i|i\in [1,n-1], a_i>a_{i+1}) max(ai∣i∈[1,n−1],ai>ai+1)
code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e5 + 10;
int n;
int a[N], m, b[N];
void solve(){
cin >> n;
for(int i = 1;i <= n;i++ ) cin >> a[i];
int ans = 0;
for(int i = 1;i < n;i++){
if(a[i] > a[i+1]){
ans = max(ans, a[i]);
}
}
cout << ans << '\n';
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t = 1;
cin >> t;
while(t--) solve();
return 0;
}
B. Good Arrays
题意:
给出一个正整数(元素均大于0)数组a,问是否存在正整数数组b,且数组b的所有元素之和与a相同,但 a i ! = b i a_i!=b_i ai!=bi
Idea:
将a数组中的非1元素全换为1,为1的就加1,剩下的数随便加到一个数上,判断是否能按该条件构造即可,n == 1 再特判一下
code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e5 + 10;
int n;
int a[N];
void solve(){
cin >> n;
for(int i = 1;i <= n;i++ ) cin >> a[i];
if(n == 1){
cout << "NO\n";
return;
}
int ned = 0, sum = 0;
for(int i = 1;i <= n;i++){
if(a[i] == 1) ned++;
else sum += a[i] - 1;
}
cout << (ned <= sum ? "YES\n" : "NO\n");
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t = 1;
cin >> t;
while(t--) solve();
return 0;
}
C. To Become Max
题意:
给一个数组a,可进行操作选择 i , 1 ≤ i ≤ n − 1 i,1 \le i \le n-1 i,1≤i≤n−1 且 a i ≤ a i + 1 a_i \le a_{i+1} ai≤ai+1,然后让 a i a_i ai 加一,最多进行k次操作,求操作后数组中最大值最多可达到多大
Idea:
数组长度最多只有1000,可以很暴力。
思考操作完的情况,如果 a i a_i ai是最后数组中的最大值,那我们一定没有选择在 a i a_i ai前面的数上操作,因为没有收益,最多只会在 a i a_i ai后面操作,因为 a i ≤ a i + 1 + 1 a_i \le a_{i+1}+1 ai≤ai+1+1。所以枚举每个数,再二分判断这个数最大可以达到多大即可
code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1010;
int n, k;
int a[N];
bool check(int i, int x){
int cnt = x, ne = a[i] + x - 1;
for(int j = i + 1;j <= n;j++, ne--){
if(a[j] >= ne) break;
if(j == n) return false; //最后一个数不能增加
cnt += ne - a[j];
}
return cnt <= k;
}
void solve(){
cin >> n >> k;
for(int i = 1;i <= n;i++ ) cin >> a[i];
int ans = a[n];
for(int i = 1;i < n;i++){
int l = 1, r = k;
while(l <= r){
int mid = l + r >> 1;
if(check(i, mid)) l = mid + 1;
else r = mid - 1;
}
ans = max(ans, a[i] + l - 1);
}
cout << ans << '\n';
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t = 1;
cin >> t;
while(t--) solve();
return 0;
}
D. More Wrong
题意:
交互题。有一个隐藏的排列,每次可询问一段区间 [ l , r ] [l,r] [l,r]的逆序对数量,花费是 ( r − l ) 2 (r-l)^2 (r−l)2,在花费不超过 5 n 2 5n^2 5n2的情况下求出数组中最大值的下标
Idea:
考虑两个相邻的数,若他们之间有一个逆序对则后一个数小,前一个数大,可如此两两询问就获得了 n / 2 n/2 n/2个可能作为答案的数。再对这 n / 2 n/2 n/2个数两两询问。
此时区间大小不是2了,如何判断两个数中那个较大?
对于每两个数,分别询问 [ l , r ] [l,r] [l,r]和 [ l , r − 1 ] [l,r-1] [l,r−1],判断两次获得的值是否相同,相同则说明去掉右端点对该区间的逆序对数量没有影响,说明右端点为该区间最大值。
如此分治询问,形如一棵线段树,算了一下花费应该不会超过 4 n 2 4n^2 4n2
code:
#include<bits/stdc++.h>
using namespace std;
int n, ret;
void ask(int l, int r){
if(l >= r){
ret = 0;
return;
}
cout << "? " << l << ' ' << r << '\n';
fflush(stdout);
cin >> ret;
}
void solve(){
cin >> n;
queue<int>q;
for(int i = 1;i <= n;i++) q.push(i);
while(q.size() > 1){
int l = q.front();
q.pop();
int r = q.front();
q.pop();
//若拿到尾部和头部端点,就把尾部放回去,重拿一个,否则代价会超出
if(l > r){
q.push(l);
l = r;
r = q.front();
q.pop();
}
ask(l, r);
if(ret == -1) return;
int s1 = ret;
ask(l, r - 1);
int s2 = ret;
if(s1 == s2){ //右端点大
q.push(r);
}else{
q.push(l);
}
}
cout << "! " << q.front() << '\n';
}
int main(){
int t;
cin >> t;
while(t--) solve();
return 0;
}
E1. PermuTree (easy version)
题意:
给出一棵根为1,有n个节点的树,每个节点的值为排列a,最多能有多少对 u , v u,v u,v 使得 a u < a l c a ( u , v ) < a v a_u < a_{lca(u,v)} < a_v au<alca(u,v)<av
Idea:
树形dp, d p [ i ] dp[i] dp[i]表示以 i 为根的子树能产生多少对贡献。
d p i dp_i dpi 首先从其儿子直接加过来,然后考虑以该点为lca产生的贡献。将子树和分成差值最小的两份,两份相乘即为新产生的最大贡献。均分子树和这个操作直接用bitset优化枚举所有子树和的情况即可
code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 5010;
int n, dp[N], siz[N];
vector<int>g[N];
void dfs(int u, int fa){
siz[u] = 1;
bitset<N>bt;
bt.set(0);
for(auto v : g[u]) if(v != fa){
dfs(v, u);
siz[u] += siz[v];
dp[u] += dp[v];
bt |= (bt << siz[v]);
}
int tmp = 0;
for(int s = 1;s <= n / 2;s++){
if(bt[s])
tmp = max(tmp, s * (siz[u] - 1 - s));
}
dp[u] += tmp;
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n;
for(int i = 2;i <= n;i++){
int p;
cin >> p;
g[i].push_back(p);
g[p].push_back(i);
}
dfs(1, 0);
cout << dp[1] << '\n';
return 0;
}