Codeforces Round #672 (Div. 2)
A. Cubes Sorting
题意: 给定一个数组,问是否能通过冒泡排序的方法,在交换 n ⋅ ( n − 1 ) 2 \frac{n·(n-1)}{2} 2n⋅(n−1)次以内将数组从小到大排序。
题解: 其实只要考虑到为什么是在 n ⋅ ( n − 1 ) 2 \frac{n·(n-1)}{2} 2n⋅(n−1)次以内,这个题目就解决了。原因在于一个极端的数组,严格地从大到小排序,此时如果我们想要通过冒泡排序将数组从小到大排序,则刚好需要交换 n ⋅ ( n − 1 ) 2 \frac{n·(n-1)}{2} 2n⋅(n−1)次。所以只要存在两个相邻的数已经严格递增,即可减少一次交换,就肯定符合题目要求。
#include<bits/stdc++.h>
using namespace std;
inline int read(){int x; cin >> x; return x;}
const int N = 5e4 + 10;
void run(){
int n = read();
int a[N];
for(int i = 0; i < n; i++) a[i] = read();
bool flag = false;
for(int i = 1; i < n && !flag; i++)
if(a[i] >= a[i - 1]) flag = true;
if(flag) cout << "YES" << endl;
else cout << "NO" << endl;
}
int main(){
int _T = read();
while(_T--) run();
return 0;
}
B. Rock and Lever
题意: 求有多少组(i,j),i<j,满足a[i] & a[j] ≥ a[i] ^ a[j]。
题解: 如果a[i],a[j]二进制最高位一样,那么就必然有a[i] & a[j] ≥ a[i] ^ a[j],否则小于。
在和同伴交流过程中,发现i < j成为了一个干扰项,其实就是两个数组队,无关先后顺序。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
inline int read(){int x; cin >> x; return x;}
const int N = 1e5 + 10;
void run(){
int n = read();
int a[N];
for(int i = 0; i < n; i++) a[i] = read();
ll cnt = 0, ans = 0;
sort(a, a + n);
for(int i = 1; i < n; ++i){
if((a[i] & a[i - 1]) >= (a[i] ^ a[i - 1]))
cnt++, ans += cnt;
else cnt = 0;
}
cout << ans << endl;
}
int main(){
int _T = read();
while(_T--) run();
return 0;
}
C1. Pokémon Army (easy version)
题意: 给定一个数组a,现在要求你选择k个索引组成的数组b ,其中
b
1
<
b
2
<
b
3
.
.
.
b
n
b_1<b_2<b_3...b _n
b1<b2<b3...bn
…,使得ab1 - ab2 + ab3 - ab4…最大。请你求出最大值。
题解: 其实一开始的困扰在于,还有一个索引数组b。其实仔细想过之后,是可以忽略不管的。因为b数组是严格递增的,所以当数组b取到了n个数之后,表明数组a的求和,相邻的两个数必然是一加一减。
没有数组b的干扰,直接就变成一道简单DP了。定义状态dp[i][0]
表示加上a[i]或不取a[i]的最大值,dp[i][1]
表示减去a[i]或不取a[i]的最大值。状态转移方程很好写直接看代码。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
inline int read(){int x; cin >> x; return x;}
const int N = 3e5 + 10;
ll a[N], dp[N][2];
void run(){
int n = read(), q = read();
for(int i = 1; i <= n; i++) a[i] = read();
for(int i = 1; i <= n; i++){
dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] + a[i]);
dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] - a[i]);
}
cout << max(dp[n][0], dp[n][1]) << endl;
}
int main(){
int _T = read();
while(_T--) run();
return 0;
}
D. Rescue Nibel!
题意: 有n盏灯,每盏灯都有一个开灯的时间l和关灯的时间r,[l,r]区间表示该灯梁着。问有k盏灯同时亮着的组合有多少种。(注意如果同一时间有4盏灯亮着,而k = 3,则组合情况应为C(4, 3) = 3
题解: 来自hr学长的思路。先将所有的灯按开灯时间从小到大排序,依次入队,入队之后按关灯时间从小到大排序。每次有新成员入队的时候,踢出所有关灯时间早于新入队的灯的开灯时间的灯。只要同一时刻队列中的灯≥k盏,需要计算此时的组合情况,最后汇总到一个sum中。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 998244353;
const int N = 1e6 + 10;
ll fac[N], inv[N];
ll qpow(ll a, ll b){
ll ans = 1;
while(b > 0){
if(b & 1) ans = ans * a % mod;
a = a * a % mod;
b >>= 1;
}
return ans;
}
ll C(ll n, ll m){
if(n < m) return 0;
return fac[n] * inv[m] % mod * inv[n - m] % mod;
}
void init (){
fac[0] = 1;
for(int i = 1 ;i < N; i++) fac[i] = (fac[i - 1] * i) % mod;
inv[N - 1] = qpow(fac[N - 1], mod - 2);
for(int i = N - 2; i >= 0 ; i--) inv[i] = inv[i + 1] * (i + 1) % mod;
}
int n, k;
struct Lamp{
int begin, end;
bool operator <(const Lamp &a)const{
return a.end < end;
}
} lamp[N];
bool cmp(Lamp &a, Lamp &b){
if(a.begin == b.begin) return a.end < b.end;
return a.begin < b.begin;
}
void run(){
cin >> n >> k;
for(int i = 0; i < n; i++){
scanf("%d%d", &lamp[i].begin, &lamp[i].end);
}
sort(lamp, lamp + n, cmp); //按开灯时间从小到大排序
ll ans = 0;
priority_queue<Lamp> q; //按关灯时间从小到大排序
for(int i = 0; i < n; i++){
int begin = lamp[i].begin;
while(!q.empty() && begin > q.top().end)
q.pop(); //踢出所有关灯过早的灯
q.push(lamp[i]);
if(q.size() >= k)
ans = (ans + C(q.size() - 1, k - 1)) % mod; //求新灯入队之后的新增组合情况
}
cout << ans << endl;
}
int main(){
init();
run();
return 0;
}