【DP】【二进制】Codeforces Round #772 (Div. 2) D.Infinite Set

107 阅读3分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

链接:
codeforces.com/contest/163…

A. Min Or Sum

只要满足两个值的或结果在某一位上为1的时候,保证两个值有一个为1即可。
所以结果为所有值的或,只要所有值该位上有1,那么结果就存在1

#include<bits/stdc++.h>
using namespace std;

void solve()
{
	int n;
	cin >> n;
	int res = 0;
	for(int i = 1; i <= n; i++)
	{
		int x;
		cin >> x;
		res |= x;
	}
	cout << res << "\n";
}
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	
	int t;
	cin >> t;
	while(t--) solve();
	return 0;
}

B. Avoid Local Maximums

构造题 目的是找不存在峰值的值,那么就对此进行讨论,可以发现如果存在峰值,就要对后面的值进行变化,如果变换峰值的话,可能后面会多出来更多的操作次数,所以需要对峰值后面的一个值进行改变。

改变的情况,如果a[i]a[i]是峰值的话,那么改变规则就是a[i+1]=max(a[i+2],a[i])a[i + 1] = max(a[i + 2], a[i])

#include<bits/stdc++.h>
using namespace std;

void solve()
{
	int n;
	cin >> n;
	vector<int> a(n + 1);
	for(int i = 1; i <= n; i++) cin >> a[i];

	int cnt = 0;
	for(int i = 2; i < n; i++)
	{
		if(a[i] > a[i - 1] && a[i] > a[i + 1])
		{
			if(i <= n - 2) cnt++, a[i + 1] = max(a[i + 2], a[i]);
			else cnt++, a[i + 1] = a[i];
		}
	}
	cout << cnt << "\n";
	for(int i = 1; i <= n; i++)
		cout << a[i] << " \n"[i == n];	
}
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	
	int t;
	cin >> t;
	while(t--) solve();
	return 0;
}

C. Differential Sorting

分情况讨论:

  • 数组本身有序,结果为0

  • a[n1]>a[n]a[n- 1] >a[n],后两个值无法操作,非降序必然不会满足

  • a[n1]<=a[n]a[n - 1]<=a[n]

    • a[n]>=0a[n]>=0,此时a[n1]a[n]a[n-1]-a[n]必然为负数,而且小于等于a[n1]a[n-1],可以让前面的每一个值等于a[n1]a[n]a[n-1]-a[n]

    • a[n]<0a[n]<0,对于最后三个数来说,a[n2]=a[n1]a[n]>a[n1]a[n-2] = a[n-1]-a[n]>a[n-1]不满足情况

#include<bits/stdc++.h>
using namespace std;

using ll = long long ;
using pii = pair<ll, int>;

void solve()
{
	int n;
	cin >> n;
	vector<ll> a(n + 1);
	vector<pii> mx(n + 1), mn(n + 1);
	for(int i = 1; i <= n; i++) cin >> a[i];
	if(is_sorted(a.begin() + 1, a.end()))
	{
		cout << 0 << "\n";
		return;
	}
	
	if(a[n - 1] > a[n]) cout << -1 << "\n";
	else
	{
		if(a[n] >= 0) 
		{
			cout << n - 2 << "\n";
			for(int i = 1; i <= n - 2; i++)
				cout << i << " " << n - 1 << " " << n << "\n";
		}
		else cout << -1 << "\n";
	}
	
}
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	
	int t;
	cin >> t;
	while(t--) solve();
	return 0;
}

D. Infinite Set

n个不同的数,可以对数组中的数进行操作,要么乘4,要么乘2加1,生成的结果形成一个集合,也可以对集合中的数再次进行操作。最终求集合中小于 2 p 2^p 2p的数的个数


首先可以发现p2e5级别的,所以正常来说并不能对2p2^p来看,然后就从二进制方面进行考虑。

对操作的转换:

  • 2x+12x+1,就是二进制右边填一个1

  • 4x4x,左移两位,二进制右边填两个0

对位数进行考虑,在目标范围内的数肯定是二进制位数小于等于p位的数

对这些数能够生成的数的个数进行求解,但是可能会有数组中有一个数通过操作可以得到数组中的另外一个数,就会出现重复,所以要进行判重

判重的逻辑:

声明一个不会重复的集合,从小到大对数组中的元素进行判断。

如果二进制后面有1就让其左移一位,有两个0的话就左移两位,否则的话就是直接退出循环。在循环中每次都判断得到的数在不重复的集合中是否出现。最后会得到一个不重复数的集合,求这些数能得到其他的数的个数。

状态表示

f[i]f[i]:二进制为i位时能够表示的数的个数

状态转移:

f[i]=f[i1]+f[i2]f[i] = f[i - 1]+f[i-2]

f[i1]f[i-1]:第i位为1时

f[i2]f[i -2]:第i位为0时

最后对某一个二进制位数的数的个数进行统计,那么该位的初始值就为出现数的个数

__lg(x) + 1x的二进制的位数

#include<bits/stdc++.h>
using namespace std;

using ll = long long ;
using pii = pair<ll, int>;
const ll mod = 1e9 + 7;

void solve()
{
	int n, p;
	cin >> n >> p;
	vector<int> a(n + 1);
	
	for(int i = 1; i <= n; i++) cin >> a[i];
	
	vector<ll> f(2e5 + 1, 0);

	sort(a.begin() + 1, a.end());
	set<int> v;
	for(int i = 1; i <= n; i++)
	{
		if(__lg(a[i]) + 1 > p) break;
		bool is = false;
		int x = a[i];
		//判断x是否能够被一个值得到
		while(x)
		{
			if(v.count(x)) is = true;
			
			if(x & 1) x >>= 1;
			else if(x & 3) break;
			else x >>= 2;
		}
		if(!is) v.insert(a[i]);
	}
	//统计相同位数出现数的个数
	vector<int> cnt(33, 0);
	for(auto i : v) cnt[__lg(i) + 1] ++;
	ll res = 0;
	
	for(int i = 1; i <= p; i++)
	{
		//初始值
		if(i <= 32) f[i] = cnt[i];
		if(i >= 2) f[i] = (f[i] + f[i - 1]) % mod;
		if(i >= 3) f[i] = (f[i] + f[i - 2]) % mod;
		//对小于等于p位的结果进行统计
		res = (res + f[i]) % mod;
	}
	cout << res << "\n";
}
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	
	int t;
//	cin >> t;
	t = 1;
	while(t--) solve();
	return 0;
}