CF做题记录
CF1685B
题意:给你一个保证合法的括号序列,其中挖空了一些(用'?'表示)既可以填 '(' 也可以填 ')' ,问是否存在唯一的填补方式
- 显然,每个合法的括号序列都有n/2个 '(' 以及n/2个 ')' 。所以要 '?' 中有n/2-cntl个左括号,右括号同理。
- 显然,对于每个右括号,其前面的左括号数量必然大于右括号数量(可以用前缀和来理解)不难发现左括号越早出现越容易符合这个性质,于是将前面n/2-cntl的'?'替成'(',后面替换成')'一定是必然出现的合法答案
- 那么,考虑次优解。同样根据上面的结论,可以看出,将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;
}

浙公网安备 33010602011771号