Codeforces Round #672 (Div. 2) Solved: 4 out of 6

Codeforces Round #672 (Div. 2)


A. Cubes Sorting

题意: 给定一个数组,问是否能通过冒泡排序的方法,在交换 n ⋅ ( n − 1 ) 2 \frac{n·(n-1)}{2} 2n(n1)次以内将数组从小到大排序。

题解: 其实只要考虑到为什么是在 n ⋅ ( n − 1 ) 2 \frac{n·(n-1)}{2} 2n(n1)次以内,这个题目就解决了。原因在于一个极端的数组,严格地从大到小排序,此时如果我们想要通过冒泡排序将数组从小到大排序,则刚好需要交换 n ⋅ ( n − 1 ) 2 \frac{n·(n-1)}{2} 2n(n1)次。所以只要存在两个相邻的数已经严格递增,即可减少一次交换,就肯定符合题目要求。

#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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值