国科信息学薪火计划模拟赛Round2题解
这套题难度比较适合,打的还行,基本上都拿到了。
A. 字符串
其实,如果先把PP都删了,后续不好删A,如果删除AP,导致序列重新拼接,反而有助于后续删除PP ,凡是AP都能删除,凡是PA就不能删了,所以优先删除AP,最后把能删除的PP都删掉,然后就最优了。如果先删除AP,其实就变为了一个括号匹配问题。
#include <bits/stdc++.h>
using namespace std;
int T,num,ans;
string s;
int main()
{ freopen("string.in","r",stdin);freopen("string.out","w",stdout);T = 1;while (T--){cin>>s;num = ans = 0;for (auto &ch : s){if (ch == 'A') num++;if (ch == 'P'){if (num > 0) num--; else ans++;}}ans = ans % 2 + num;cout<<ans<<endl;}return 0;
} 请不要抄代码,害人害己
B. 翻转
显然,每个位置至多翻转一次。观察发现题目的数据范围很小,可以考虑比较暴力的方法。因为要找最少次数,也没有明显的规律来贪心,其实就要往搜索的方向去思考。找到所有情况的操作次数求最小值就是答案。
如何搜索呢???
我们先考虑对于第一行到底如何操作,第一行一共M个格子,每个格子都可以考虑是否翻转一下,因此总共2m2^m2m种方法
第一行翻转后,如何确保最终能变得全凹进去呢??我们翻转一个格子,带来的影响是它本身,以及上下左右的翻转,因此可以利用下一行格子来控制本行翻转为凹 。所以也就是靠控制第iii行第jjj列的格子翻转来实现使得第i−1i-1i−1行第jjj列格子能否凹进去
具体如何实现??(i−1,j)(i-1,j)(i−1,j)位置的格子状态由它初始状态,它的翻转状态,以及它上面格子,左边格子,右边格子的翻转状态决定,如果综合下来是凹的,那么(i,j)(i,j)(i,j)就不需要操作了;否则就要翻转(i,j)(i,j)(i,j) 。最终根据这个性质完成整个矩阵的翻转,判断能不能实现。如果能实现就要求操作次数最小值。
总时间复杂度O(2m×n×m)O(2^m \times n \times m)O(2m×n×m)
/*
freopen("string.in","r",stdin);
freopen("string.out","w",stdout);
*/
#include<bits/stdc++.h>
using namespace std;
int n,m,a[30];请不要抄代码,害人害己
int mapp[30][30];请不要抄代码,害人害己
int vis[30][30];请不要抄代码,害人害己
char c;请不要抄代码,害人害己
int ans,num;请不要抄代码,害人害己
void shoot(int x,int y){请不要抄代码,害人害己vis[x][y]=!vis[x][y];vis[x-1][y]=!vis[x-1][y];vis[x+1][y]=!vis[x+1][y];vis[x][y-1]=!vis[x][y-1];vis[x][y+1]=!vis[x][y+1];num++; 请不要抄代码,害人害己
}请不要抄代码,害人害己
void dfs(int now){if(now > m){num=0;for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)vis[i][j]=mapp[i][j];for(int i=1;i<=m;i++) if(a[i]) shoot(1,i);for(int i=2;i<=n;i++)for(int j=1;j<=m;j++)if(vis[i-1][j]==1) shoot(i,j);bool t_=1;for(int i=1;i<=m;i++) if(vis[n][i]) t_=0;if(t_) ans=min(ans,num);return ;}请不要抄代码,害人害己a[now]=0;请不要抄代码,害人害己dfs(now+1);a[now]=1;请不要抄代码,害人害己dfs(now+1);
}请不要抄代码,害人害己
int main(){freopen("flip.in","r",stdin);
freopen("flip.out","w",stdout);cin >> n >> m;for(int i=1;i<=n;i++)for(int j=1;j<=m;j++){cin >> c;mapp[i][j]=(c=='X');}请不要抄代码,害人害己ans=n*m;dfs(1);cout << ans; 请不要抄代码,害人害己
}
C题 索引
暴力得分就直接模拟,拿满分有一定难度
暴力写法参考
#include<bits/stdc++.h>
using namespace std;
int k , n;
bool p;
int a[10000005];
int main(){freopen("index.in","r",stdin);freopen("index.out","w",stdout);ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);cin >> k >> n;p = false;for(int i = 1; i <= n; i++){cin >> a[i];if(i == a[i]){p = true;}}if(p){cout << "YES\n";}else{cout << "NO\n";}k--;int l , r , c;while(k--){p = false;cin >> l >> r >> c;for(int i = 1; i <= n; i++){if(i >= l && i <= r){a[i] += c;}if(a[i] == i){p = true;}}if(p){cout << "YES\n";}else{cout << "NO\n";}}
// fclose(stdin);
// fclose(stdout);return 0;
}
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#pragma GCC optimize(3, "Ofast", "inline")
#define fir(i, a, b) for (int i = a; i <= b; i++)const int maxn=1e7+10;
const int MAX_INF=0x3f3f3f3f;
int a[maxn];
int k,n;
struct node
{int l;int r;int val;
};
#define left (i<<1)
#define right (i<<1|1)
node seg[maxn*4];
void change_node(int i,int val){seg[i].val+=val;
}
void push_down(int i){if(seg[i].val){change_node(left,seg[i].val);change_node(right,seg[i].val);seg[i].val=0;}
}
void build(int l,int r,int i)
{seg[i].l=l;seg[i].r=r;if(l==r){seg[i].val=a[l];return;}int mid=(l+r)>>1;build(l,mid,left);build(mid+1,r,right);
}
void change(int l,int r,int i,int val){if(l<=seg[i].l&&seg[i].r<=r){change_node(i,val);return;}push_down(i);int mid=(seg[i].l+seg[i].r)>>1;if(l<=mid)change(l,r,left,val);if(r>mid)change(l,r,right,val);
}
int query(int x,int i){if(seg[i].l==seg[i].r&&seg[i].l==x)return seg[i].val;push_down(i);int mid=(seg[i].l+seg[i].r)>>1;if(x<=mid)return query(x,left);return query(x,right);
}
void work()
{int l=1,r=n+1;while(l<r){int mid=(l+r+1)>>1;if(query(mid,1)>mid)r=mid-1;else l=mid;}if(query(l,1)==l)puts("YES");else puts("NO");
}
int main()
{ scanf("%d%d",&k,&n);fir(i,1,n)scanf("%d",&a[i]);a[n+1]=MAX_INF;build(1,n+1,1);work();k--;while(k--){int l,r,c;
// cin>>l>>r>>c;scanf("%d%d%d",&l,&r,&c);change(l,r,1,c);work();}
} 请不要抄代码,正解写法
#include <bits/stdc++.h>
using namespace std;// 题意转化:B[i] = A[i] - i 单调不降
// 问是否存在 i 使得 B[i] + sum_{覆盖i的修改} C == 0
// 做法:朴素累加所有历史区间加(K ≤ 1000),配合二分找第一个 B[i] >= 0 再判等。const int MAXK = 1000 + 5;int main() {ios::sync_with_stdio(false);cin.tie(nullptr);int K, n;cin >> K >> n;// base[i] = 初始的 A[i] - ivector<int> base(n + 1);for (int i = 1; i <= n; ++i) {long long a; // 防止读入时中间溢出cin >> a;base[i] = int(a - i);}// 记录历史修改(不需要高级数据结构,直接数组存)int L[MAXK], R[MAXK], C[MAXK];int tot = 0; // 已生效的修改次数auto valueAt = [&](int i) -> long long {long long v = base[i];// 把所有覆盖 i 的区间加上去(K 最多 1000,开销可接受)for (int t = 1; t <= tot; ++t) {if (L[t] <= i && i <= R[t]) v += C[t];}return v;};auto hasFixedPoint = [&]() -> bool {// 单调不降函数的边界剪枝if (valueAt(1) > 0) return false;if (valueAt(n) < 0) return false;// 二分找第一个 valueAt(i) >= 0int l = 1, r = n, pos = n + 1;while (l <= r) {int mid = (l + r) >> 1;if (valueAt(mid) >= 0) {pos = mid;r = mid - 1;} else {l = mid + 1;}}return (pos <= n && valueAt(pos) == 0);};// 第 1 次询问:没有修改,直接判断cout << (hasFixedPoint() ? "YES\n" : "NO\n");// 后续 K-1 次:先记录本次修改,再判断for (int q = 2; q <= K; ++q) {++tot;cin >> L[tot] >> R[tot] >> C[tot];cout << (hasFixedPoint() ? "YES\n" : "NO\n");}return 0;
}
D. 修改序列
其实题目看透之后会发现,这就是个最长上升子序列 NlogNNlogNNlogN写法
当然如果不会,就写N2N^2N2的
N方暴力
#include<bits/stdc++.h>
using namespace std;
int n,a[100100],dp[100100],m;
int main(){freopen("sequence.in","r",stdin);freopen("sequence.out","w",stdout);cin>>n;for(int i=1;i<=n;i++){cin>>a[i];}for(int i=1;i<=n;i++)dp[i]=1;for(int i=2;i<=n;i++){for(int j=1;j<i;j++){if(a[i]>a[j]){dp[i]=max(dp[i],dp[j]+1);}}m=max(m,dp[i]);}cout<<m;return 0;
}
正解 100分
#include<bits/stdc++.h>
using namespace std;
int n;
int a[100005] , dp[100005];
int main(){freopen("sequence.in","r",stdin);freopen("sequence.out","w",stdout);cin >> n;for(int i = 1; i <= n; i++){cin >> a[i];}dp[1] = a[1];int len = 1;for(int i = 1; i <= n; i++){if(a[i] > dp[len])dp[++len] = a[i];else{int l = 1 , r = len;while(l < r){int mid = (l + r) / 2;if(dp[mid] >= a[i]) r = mid;else l = mid + 1;}dp[r] = a[i];}}cout << len;fclose(stdin);fclose(stdout);return 0;
}