CF做题记录

CF1685B

题意:给你一个保证合法的括号序列,其中挖空了一些(用'?'表示)既可以填 '(' 也可以填 ')' ,问是否存在唯一的填补方式

  1. 显然,每个合法的括号序列都有n/2个 '(' 以及n/2个 ')' 。所以要 '?' 中有n/2-cntl个左括号,右括号同理。
  2. 显然,对于每个右括号,其前面的左括号数量必然大于右括号数量(可以用前缀和来理解)不难发现左括号越早出现越容易符合这个性质,于是将前面n/2-cntl的'?'替成'(',后面替换成')'一定是必然出现的合法答案
  3. 那么,考虑次优解。同样根据上面的结论,可以看出,将n/2-cntl的'?'替成')',n/2-cntl+1的'?'替成'('是次优解。

注意!以上结论是在题目保证括号序列合法的前提下推出!

#include <cstdio>
#include <iostream>
#include <stack>
using namespace std;

bool ch(string str) {
	stack<int> stc;
	for (int i = 0; i < str.size(); i++) {
		if (str[i] == '(') stc.push(i);
		else if (stc.empty()) return false;
		else stc.pop();
	}
	return true;
}

int main() {
	int T; scanf("%d",&T);
	while (T--) {
		string s; cin >> s;
		int n = s.size(), lc = 0, rc = 0;
		for (int i = 0; i < s.size(); i++) {
			if (s[i] == '(') lc++;
			else rc++;
		}
		lc = n/2 - lc, rc = n/2 - rc;
		if (!lc || !rc) {
			printf("YES\n");
			continue;
		}
		int cnt = 0;
		for (int i = 0; i < s.size(); i++) {
			if (s[i] == '?') {
				++cnt;
				if (cnt < lc) s[i] = '(';
				else if (cnt == lc) s[i] = ')';
				else if (cnt == lc+1) s[i] = '(';
				else s[i] = ')';
			}
		}
		if (ch(s)) printf("NO\n");
		else printf("YES\n");
	}
	return 0;
}

CF1635C

给定一个长度为 n 的数组 a。你可以执行不超过 n 次操作,每次操作中,你可以选择三个整数 x,y,z,需要保证 1⩽x<y<z⩽n, 将a[x]替换为a[y] - a[z]。目标是使得最终的数组按照非降序排列。请给出你所构造的方案的操作次数,并给出每次操作中你选择的三个整数;或者不存在可行方案,输出-1

注意构造思维的运用。
结论是先判定a[n-1]是否小于a[n],然后将所有的a[i]替换为a[n-1]-a[n]

#include <cstdio>
using namespace std;
const int maxn = 2e5+100;
const int INF = 1e8;
int a[maxn];
int main() {
	int T; scanf("%d",&T);
	while (T--) {
		int n; scanf("%d",&n);
		bool flag = true;
		for (int i = 1; i <= n; i++) {
			scanf("%d",&a[i]);
			if (i > 1 && a[i] < a[i-1]) flag = false;
		}
		if (flag) {
			printf("0\n");
			continue;
		}
		if (a[n-1] > a[n] || a[n] < 0) {
			printf("-1\n");
			continue;
		}
		// if (a[n-1] - a[n] < -INF) {
		// 	printf("-1\n");
		// 	continue;
		// }
		printf("%d\n",n-2);
		for (int i = 1; i < n-1; i++) {
			printf("%d %d %d\n",i,n-1,n);
		}
	}
	return 0;
}

CF1635D

题目链接
差最后一步就自己想出来了。。。
考虑题目条件,第一个相当于在二进制后加一个1,第二个相当于加两个0
问题就变成了在每一个a[i]后面补东西使其变成小于等于p位的计数
自然想到了dp
要注意一下最后答案是前缀和

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int maxn = 2e5+100;
const int mod = 1e9+7;
int n,p;
vector<int> num;
map<int, bool> mp;
int a[maxn], f[maxn][35], pre[maxn];

int cntdig(int x) {
	int dig = 0;
	while (x) x /= 2, dig++;
	return dig;
}

signed main() {
	scanf("%lld%lld",&n,&p);
	for (int i = 1; i <= n; i++) scanf("%lld",&a[i]);
	sort(a+1,a+1+n);
	for (int i = 1; i <= n; i++) {
		int cur = a[i];
		bool flag = false;
		while(cur > 0) {
			if (mp[cur]) {
				flag = true;
				break;
			}
			if (cur % 2) cur /= 2;
			else if (cur %  4 == 0) cur /= 4;
			else break;
		}
		if (flag) continue;
		else {
			mp[a[i]] = true;
			num.push_back(a[i]);
		}
	}
	memset(f,0,sizeof(f));
	f[1][1] = 1, f[2][0] = 1, f[2][1] = 1;
	pre[1] = 1, pre[2] = 3;
	for (int i = 3; i <= p; i++) {
		f[i][0] = (f[i-2][0] + f[i-2][1]) % mod;
		f[i][1] = (f[i-1][0] + f[i-1][1]) % mod;
		pre[i] = (pre[i-1]+f[i][0]+f[i][1]) % mod;
	}
//	for (int i = 1; i <= p; i++) {
//		printf("f[%d][0] = %d, f[%d][1] = %d\n",i,f[i][0],i,f[i][1]);
//	}
	int ans = 0;
	for (int i = 0; i < num.size(); i++) {
		int need = p-cntdig(num[i]);
		if (need < 0) continue; 
//		cout << cntdig(num[i]) << endl;
		ans = (ans + pre[need] + 1) % mod;
	}
	printf("%lld\n",ans);
	return 0;
}

CF1203F

这道题还是十分值得学习的
题目链接
首先,对于上分的部分,我们尽可能多的拿,直接贪心即可。
对于掉分的部分,需要仔细思考🤔
(以下文字可以在假设全拿的语境下阅读)首先我们考虑拿项目顺序。毕竟拿一个项目会掉一次分,掉分的多少直接关系到能不能拿其他项目。考虑最优顺序时要运用到邻项交换排序思想。
拿一个项目所需要的rating自然是越少越好。考虑拿第i个项目需要的rating值(记为rr),不难发现除了自身属性(a[i]),它仅仅与上一个物品(b[j])掉的分有关。所以想得到全局的最优策略,我们两两分组讨论即可。
对于每一组相邻的项目,都可以将它们表示为a[i],b[i]以及a[j],b[j]
令当前rating为r,所以
当i在前时,r >= a[i], r + b[i] >= a[j] 即 r >= a[j] - b[i]
当j在前时,r >= a[j], r + b[j] >= a[i] 即 r >= a[i] - b[j]
所以
r >= max(a[i], a[j] - b[i]) 或 r >= max(a[j], a[i] - b[j])
我们强行令i在前更优,那么
max(a[i], a[j] - b[i]) < max(a[j], a[i] - b[j])
显然,a[i] < a[i] - b[j] 且 a[j] - b[i] > a[j]
所以需要满足 a[i]-b[j] > a[j] - b[i],即a[i]+b[i] > a[j]+b[j]
也就是说,我们按照a[i]+b[i] > a[j]+b[j]排序,得到的就是最优的拿取顺序。
OK,顺序想完了,现在想怎么拿。
这不是一眼dp吗
既然懂,就不多写了,上代码

#include <cstdio>
#include <algorithm>
#include <iostream>
using namespace std;
using namespace std;
const int maxn = 200;
const int maxr = 40000;
int n,r,ans,psz1,psz2;
int f[maxn][maxr];
const int INF = 0x3f3f3f3f;

struct Pos{
	int a,b;
	bool operator < (const Pos&other) const {
		return a < other.a;
	}
}po[maxn];

struct Nag{
	int a,b;
	bool operator < (const Nag&other) const {
		return a+b > other.a+other.b;
	}
}na[maxn];

int main() {
	scanf ("%d%d",&n,&r);
	for (int i = 1; i <= n; i++) {
		int a,b; scanf("%d%d",&a,&b);
		if (b >= 0) po[++psz1].a = a, po[psz1].b = b;
		else na[++psz2].a = a, na[psz2].b = b;
	}
	sort(po+1,po+1+psz1);
	sort(na+1,na+1+psz2);
	for (int i = 1; i <= psz1; i++) if (r >= po[i].a) ans++, r += po[i].b;
//	cout << ans << endl;
	int cur = 0;
	//dp, f[i][j]:project i, rate j, mx prj numbers
	//f[i][j] = max(f[i-1][j],f[i-1][j-b[i]]+1)
	for (int i = 0; i < r; i++) f[0][i] = -1; 
	for (int i = 1; i <= psz2; i++) {
		//j means current rate
		for (int j = 0; j <= r+na[i].b; j++) {
			f[i][j] = max(f[i][j],f[i-1][j]);
			if (j-na[i].b >= na[i].a) f[i][j] = max(f[i][j],f[i-1][j-na[i].b]+1);
			cur = max(cur,f[i][j]);
		}
	}
	printf("%d\n", ans+cur);
}
//solution is damned. 'd better code by yourself

CF1278D

给爷整破防了
刚开始没想清楚,码了个set没用好,调了半天出不来。。。
其实一点都不难,本质上类似于偏序问题。
题意翻译过来就是求l[i] <l[j] 且 r[i] > l[j] 且 r[i] < r[j]的i,j对数
很容易想到先根据l[i]排个序,那我们就显然保证了左端点是上升的。
那么现在问题就转化为了对于现在的 r[i] 往前找r[i] > r[j]个数
那就吧之前的r全丢进一个set里即可
但是,真的别tm用pair了
正正负负的tm绕死人
不如整个结构体
对了,最后要判一下是不是树

#include <cstdio>
#include <iostream>
#include <set>
#include <cstdlib>
#include <vector>
#include <algorithm>
using namespace std;
const int maxn = 5e5+1000; 
bool vis[maxn];
vector<int> adj[maxn];
int n, cnt;
struct Line{
	int l,r;
	bool operator<(const Line &other) const {
		return l < other.l;
	}
}L[maxn];
struct Element{
	int v,id;
	bool operator < (const Element &x) const {
		return x.v>v;	
	}
};
set<Element> bst;
void dfs(int u, int fa) {
	vis[u] = 1;
	for (auto v:adj[u]) if (!vis[v]) dfs(v,u);
}
bool istree() {
	dfs(1,0);
	for (int i = 1; i <= n; i++) if (!vis[i]) return false;
	return true;
}
int main() {
	scanf("%d",&n);
	for (int i = 1; i <= n; i++) scanf("%d%d",&L[i].l,&L[i].r);
	sort(L+1,L+1+n);
	bst.insert((Element){L[1].r, 1});
	for (int i = 2; i <= n; i++) {
		set<Element>::iterator it = bst.lower_bound((Element){L[i].l, 1});
		while (it != bst.end()) {
			if (it->v >= L[i].r) break;
			cnt++;
			adj[i].push_back(it->id), adj[it->id].push_back(i);
			if (cnt == n) {
				printf("NO\n");
				return 0;
			}
			it++;
		}
		bst.insert((Element){L[i].r, i});
	}
	// cout << "edge: " << endl;
	// for (int i = 1; i <= n; i++) {
	// 	for (auto v:adj[i]) printf("%d ",v);
	// 	printf("\n");
	// }
	if (istree()) printf("YES\n");
	else printf("NO\n");	
	return 0;
}
posted @ 2022-08-09 23:01  mossy  阅读(14)  评论(0)    收藏  举报