1.4 vp Codeforces Round #838 (Div. 2)

A - Divide and Conquer

题意:给出序列a,设b为a中元素总和。你可以选择a中任意元素,将它除以二(向下取整)。问最少需要多少次可以使b为偶数
思路:将a划分为奇偶两个集合。a中偶数元素的数量是奇是偶对题目没有影响,要使b为偶数,需要知道奇数元素的个数
若奇数元素是偶数,则b一开始就是偶数,输出0
否则
1.对于奇数集合中每个元素进行操作,找出可以使一个元素变成偶数需要的最少操作x
2.对于偶数集合中每个元素进行操作,找出可以使一个元素变成奇数需要的最少操作y
ans = min(x,y)

const int N = 50 + 10, M = 5010, INF = 2e9, MOD = 1e9 + 7;
int a[N];
 
//注意观察N的大小
void solve() {
	int n;
	cin >> n;
	int cnt0 = 0, cnt1 = 0;
	for (int i = 1; i <= n; i ++) {
		cin >> a[i];
		if (a[i] % 2 == 1) cnt1 ++;
		else cnt0 ++;
	}
	
	if (cnt1 % 2 == 0) {
		cout << 0 << endl;
	}else {
		int ans = 1e9;
		for (int i = 1; i <= n; i ++) {
			if (a[i] % 2 == 1) {
				int t = 0;
				while (a[i] % 2 != 0) {
					t ++;
					a[i] /= 2;
				}
				ans = min(ans, t);
			}else {
				int t = 0;
				while (a[i] % 2 != 1) {
					t ++;
					a[i] /= 2;
				}
				ans = min(ans, t);
			}
		}
		
		cout << ans << endl;
	}
}

B - Make Array Good

题意:给出序列a,你可以进行操作,每次操作可以使a中一个元素加上小于等于它的一个数x,(x>0)。要求操作数不超过n,且要输出最终操作次数和每次操作的下标和x
思路:找到序列中最小的元素mina,然后后面元素都以它为最小因子进行增添即可。若a中存在mina的倍数或者存在某元素操作后为mina的倍数,则将mina更新为较大的那个。

const int N = 1e5 + 10, M = 5010, INF = 2e9, MOD = 1e9 + 7;
PII a[N];
 
//注意观察N的大小
void solve() {
	int n;
	cin >> n;
	for (int i = 1; i <= n; i ++) {
		cin >> a[i].first;
		a[i].second = i;
	}
	sort(a + 1, a + 1 + n);
	int tmp = a[1].first;
	vector<PII> ans;
	for (int i = 2; i <= n; i ++) {
		if (a[i].first % tmp == 0) {
			if (a[i].first > tmp) tmp = a[i].first;
		}else {
			int t = a[i].first / tmp;
			int x = tmp * (t + 1) - a[i].first;
			tmp = tmp * (t + 1);
			ans.push_back({a[i].second, x});
		}
	}
	
	cout << ans.size() << endl;
	for (auto [x, y] : ans) cout << x << ' ' << y << endl;
}

C. Binary Strings are Fun

题意:给出一个二进制字符串,你可以将它“扩充”,即在每个元素中间增加一个二进制数(0/1),若ai在(1,i)上是中位数,那么说这个前缀是好的。问从(1,n)中将其扩充后可以有多少好的前缀
思路:找规律。如01,10这类,中间若要扩充只可能有一种情况,而11,00这类可以有两种情况。前面已经确定的序列对后面也有影响。

void solve() {
	int n;
	cin >> n;
	string s;
	cin >> s;
	LL sum = 1, tmp = 1;                                  //方案总数sum,过程方案数tmp
	for (int i = 1; i < s.size(); i ++) {
		int x = s[i - 1] - '0', y = s[i] - '0';
		if (x != y) tmp = 1;                         //如果相邻两个数不相同,那么从开始到当前位置仅有一种可能的方案
		else tmp *= 2;                               //若相同,那么方案数乘以当前位置扩充的可能
		sum += tmp;
		sum %= MOD;
		tmp %= MOD;
	}
	
	cout << sum << endl;
}
 

D - GCD Queries

题意:交互题,给出一个n,代表有一个含有n个数字的排列。这个排列是隐藏的,需要通过交互找到数字0的位置。每次提问给出两个数的下标,会回答这两个数的gcd,若这两个数:x,y中有一个为0则可以输出
思路:直接去找到0很难,那可以用排除法,将不是0的数字排除,剩下的就只有0
设有下标l, mid, r。每次进行两次询问x = gcd(l, mid), y = gcd(mid, r)。
若x == y,则mid所代表的元素绝不可能为0
若x > y,则x所代表的元素不可能为0,
若y > x,则y所代表的元素不可能为0.

ps:如果关了同步流需要注释掉这部分,否则会出现

void ask(int x, int y) {
	cout << "? " << x << " " << y << endl;
}
 
//注意观察N的大小
void solve() {
	int n;
	cin >> n;
	queue<int> q;
	for (int i = 1; i <= n; i ++) q.push(i);
	while (q.size() > 2) {
		int l = q.front();
		q.pop();
		int r = q.front();
		q.pop();
		int mid = q.front();
		q.pop();
		ask(l, mid);
		int x, y;
		cin >> x;
		ask(mid, r);
		cin >> y;
		if (x == y) {
			q.push(l);
			q.push(r);
		}else if (x > y) {
			q.push(l);
			q.push(mid);
		}else {
			q.push(r);
			q.push(mid);
		}
	}
	
	int x, y;
	x = q.front(), q.pop();
	y = q.front(), q.pop();
	cout << "! " << x << " " << y << endl;
	cin >> n;
}

总结:这四道题出现了两道构造,一道计数类,一道交互。感觉构造类有些想法,无非划分集合,根据题目特性找到数据关联之类的。计数类问题也需要分类讨论,然后累加。交互类问题很薄弱,拿到手基本没什么思路,其实只需要直指问题核心即可。

posted @ 2023-01-04 15:48  nobodyL  阅读(30)  评论(0)    收藏  举报