赛时没做出来的题莫慌,记得赛后补题就行。
签到题:1,6,7,10
简单题:4,9
困难题:2,5
其他都是中等题。
01.喜不喜欢开门红
纯纯的签到,标准的送分题(这个题是直接拉的cf的,可能翻译的有的怪了,刚开始以为大家应该都能理解的,赛时突然发现有人好像理解错了,我的问题吧)
统计一下字母出现的次数,然后如果有某个字母没出现过,输出NO,否则输出YES
#include <iostream>
#include <map>
#include<algorithm>
typedef long long ll;
using namespace std;
char a[105];
ll b[200];
int main()
{
ll k = 1;
ll n;
cin >> n;
for (ll i = 0; i < n; i++)
{
cin >> a[i];
if (a[i] >= 'a' && a[i] <= 'z')
a[i] -= 'a' - 'A';
}
for (int i = 0; i < n; i++)
b[a[i]]++;
for (int i = 'A'; i <= 'Z'; i++)
{
if (b[i] == 0)
{
k = 0;
break;
}
}
if (k == 0)
cout << "NO";
else
cout << "YES";
system("pause");
return 0;
}
02.禹儿口中的180分
这个题,题目的意思其实很简单,就是要得到一个0000…111…序列
两种操作 1:0与1交换 条件连续 代价1e12
2:删除一个字符 代价1e12+1
求最小代价
很明显直接暴力操作会超时,那么就要优化了
在这题中呢,我们用到了前缀和 和 后缀和去进行优化
要形成的是一个以0开头以1结尾的字符串,那么我们可以进行枚举每一个01的交接地方,然后将前边的1删除,后边的0删除,切记交换的代价要小,很明显,交换的次数是很好判断的
Ok,直接上代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6;
int h[N]; // 前缀 1 的个数
int b[N]; // 后缀 0 的个数
int q[N];
int re[N]; // 答案
int re1 = 1e12;
int re2 = 1e12 + 1;
void solve()
{
string s;
cin >> s;
int n = s.size();
for (int i = 0; i < n; i++)
q[i] = s[i] - '0';
int l = 0;
for (int i = 0; i < n; i++)
{
if (q[i] == 1)
l++;
h[i] = l;
}
l = 0;
for (int i = n - 1; i >= 0; i--)
{
if (q[i] == 0)
l++;
b[i] = l;
}
re[0] = b[0] * re2;
re[n] = h[n - 1] * re2;
for (int i = 1; i <= n - 1; i++)
{
if (q[i - 1] == 1 && q[i] == 0)
re[i] = re1 + (h[i - 1] + b[i] - 2) * re2;
else
re[i] = (h[i - 1] + b[i]) * re2;
}
int res = 1e18;
for (int i = 0; i <= n; i++)
{
res = min(res, re[i]);
re[i] = 0;
}
cout << res << endl;
}
signed main()
{
int _;
_ = 1;
cin >> _;
while (_--)
{
solve();
}
}
03.冰冰转转盘
这个题,首先我们算一下时间复杂度,如果我们每一次都进行遍历m次,那么肯定会超时,这时候,我们可以想到肯定不能遍历,那就要进行优化,对于这个题优化的方法很简单就是找到循环节,转盘指针一直在旋转,那么是不是会存在一个循环,使得我们转到这个位置之后回到原位置,同时后边进行循环,在这里我们找到的循环节是2n,至于怎么找到的,你们可以证明一下,你们n的大小和最多是2e5,循环次数最多是4e5,时间符合,那就可以直接写了
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6;
int q[N];
void solve()
{
int n, m, k;
cin >> n >> m >> k;
for (int i = 1; i <= min(2 * n, k); i++)
{
if ((m - n + (i * i + i) / 2) % n == 0)
{
cout << "YES\n";
return;
}
}
cout << "NO" << endl;
}
signed main()
{
int _;
_ = 1;
cin >> _;
while (_--)
{
solve();
}
}
04.这题你是红了还是绿了
这题明明草稿纸简单模拟写一下就能找到规律的,真不应该红的;
结论:如果能找出两个数的差等于k,那么一定可以通过n-1次操作得到k,否则一定不能;
就比如这个数组的值等于a,b,c,x;
去掉x,变成a-x,b-x, c-x
再去掉a-x,就变成 b-a,c-a;
再去掉b-a,就得到 c-b;
有没有发现减掉的部分是可以相互约去的, 那么留到最后的值一定是两个值的差(n=1除外),在这个过程你可以自行选择先后顺序,意味着你可以任意选择两个值的差作为最终结果;
代码处理:如果通过两个for循环暴力会TLE,所以可以考虑用加法,或者双指针,
双指针做法:
#include<iostream>
#include<algorithm>
#define int long long
using namespace std;
const int N = 200050;
int a[N];
signed main(){
int T,n,k;
cin>>T;
while(T--)
{
cin>>n>>k;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
bool ans = false;
if(n == 1) ans = (a[1] == k);
else
{
sort(a+1,a+n+1);//给数组从小到大排序; 枚举每个右端点j的左端点i的可能答案;
int i = 1 , j = 2;
while(j <= n && i <= n)
{
if(abs(k) == a[j] - a[i])
{
ans = true;
break;
}
else if(abs(k) < a[j] - a[i]) i++;//如果后面的数a[j]距离已经超过k的绝对值, 那就通过增大i来增大a[i],也就是缩短a[j]-a[i]的距离,
else j++; //反之距离小了就增大
}
}
if(ans == true)
{
printf("YES\n");
}
else printf("NO\n");
}
return 0;
}
用map的加法
#include <bits/stdc++.h>
using namespace std;
const int N = 2e6+10;
string s;
char ch;
int t,n,a[N],k;
int main()
{
cin>>t;
while(t--)
{
cin>>n>>k;
map<int,bool>mp;
for(int i=1;i<=n;i++)
{
cin>>a[i];
mp[a[i]]=true;
}
int fg=0;
for(int i=1;i<=n;i++)
{
if(mp[a[i]]&&mp[a[i]+k])
{
fg=1;break;
}
}
if(fg)cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
return 0;
}
05.异或和
因为ai的数据范围只到500,在二进制中能影响到的位数只有前9位,也就是说最大的答案只有512个,那么我们就可以暴力枚举ai,和ai之前产生的异或值,时间复杂度为1e5*512,大约5e7
求递增子序列的异或和,我们可以用一个mi数组来存,mi[i]代表异或值为i时的最后一个数的大小,因为是要最后一个数要小于当前遍历的ai才能异或这个ai,所以当异或值同样的时候,我们只需记录最后一个数最小的情况就行。
#include<iostream>
using namespace std;
const int N=5e5+10;
bool st[515];//把异或和出现过的值标记为1;
int a[N],mi[515];//mi[i]表示异或和的值为i时最后一个数大小为mi[i]。
signed main(){
int n;
cin>>n;
for(int i=0;i<=512;++i)mi[i]=600;//初始化
for(int i=1,x;i<=n;++i)
{
cin>>x;
for(int j=1;j<=512;++j)
{
if(st[j]&&mi[j]<x)
{
st[j^x]=1;
mi[j^x]=min(mi[j^x],x);
}
}
st[x]=1;
mi[x]=min(mi[x],x);
}
int sum=1;//0是空序列产生的异或和,所以至少有一个答案的。
for(int i=1;i<=512;++i)sum+=st[i];
cout<<sum<<'\n'<<"0 ";
for(int i=1;i<=512;++i)
{
if(st[i])cout<<i<<" ";
}
return 0;
}
06 签到?“真”签到
这个题就是签到了,是不是第一次见到这么诚实学长,嘿嘿
证明大于12的数由两个合数想加得到
首先对于偶数 我们找到4 与 n-4
对于奇数 我们找到9 与 n-9
至于我们是怎么找到了,你可以理解为猜出来的,这个,我也不会证明(QAQ
(注意数据范围哦)
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6;
int q[N];
void solve()
{
int n;
cin >> n;
if (n % 2)
cout << "9 " << n - 9 << endl;
else
cout << "4 " << n - 4 << endl;
}
signed main()
{
int _;
_ = 1;
// cin >> _;
while (_--)
{
solve();
}
}
07.你会跑步吗(?
全场最签到的一题
输入四个数,判断,记录比第一个数大的数的个数,输出
#include <algorithm>
#include <iostream>
using namespace std;
typedef long long ll;
#define INF 0x3f3f3f3f
void solve()
{
int n,m;
cin>>n;
int res=0;
for(int i=0;i<3;i++)
{
cin>>m;
if(m>n)res++;
}
cout<<res<<endl;
}
int main()
{
int t;
cin >> t;
while (t--)
{
solve();
}
system("pause");
return 0;
}
08.哇哇哇哇哇哇哇哇哇
贪心
由于a为非递减序列,则最大得分的子序列一定为它的后缀序列。易得后缀序列的得分计算为(a1/k)*(a2/k-1)*...(ak/1)。
由于得分要最大,所以后缀数组中的每一个元素都要大于等于1,二分最大长度,判断条件就是
a[i-mid+1]>=mid (a[i-mid+1]/mid>=1)
也可用双指针求解
//二分
#include<iostream>
using namespace std;
int a[100005];
int main() {
int t;
cin>>t;
while(t--) {
int n;
cin>>n;
for(int i=1; i<=n; i++) {
cin>>a[i];
}
for(int i=1; i<=n; i++) {
int l=0,r=i+1;
while (l + 1 != r) {
int mid = (l + r)/2;
if (a[i - mid + 1] >= mid) {
l = mid;
} else r = mid;
}
cout<<l<<" ";
}
cout<<endl;
}
}
//双指针
#include<iostream>
using namespace std;
int a[100005];
int main() {
int t;
cin>>t;
while(t--) {
int n;
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
int cnt = 0;
int k = 1;
for (int i = 1; i <= n; i++) {
if (a[k] >= cnt + 1) {
cnt++;
} else {
k++;
}
cout << cnt << " ";
}
cout << endl;
}
}
09.吱吱吱吱吱吱吱吱吱
//简单的染色题
//我们每遍历到一处水深不为0的点,将与它连通的所有水深不为0的点都进行标记(vis[x][y]=1),并累计每个点的贡献即可
//用dfs或bfs都可,记得取最大值
#include<iostream>
using namespace std;
int vis[1005][1005]; //表示有没有被访问过
int a[1005][1005];
int dx[4] = { 0,1,0,-1 };
int dy[4] = { 1,0,-1,0 };
int n, m;
int dfs(int x,int y) {
vis[x][y] = 1;
int sum = a[x][y];
for (int k = 0; k < 4; k++) {
int xx = x + dx[k];
int yy = y + dy[k];
if (vis[xx][yy] == 0 && xx >= 1 && xx <= n && yy >= 1 && yy <= m && a[xx][yy] != 0) {
sum += dfs(xx, yy);
}
}
return sum;
}
int main() {
int t;
cin>>t;
while(t--) {
cin >> n >> m;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
cin >> a[i][j];
vis[i][j] = 0;
}
}
int ans = 0;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (vis[i][j] == 0 && a[i][j] != 0) {
ans = max(ans, dfs(i, j));
}
}
}
cout << ans << endl;
}
}
10.嘤嘤嘤嘤嘤嘤嘤嘤嘤
签到题,只要两端的字符不同就可以删去,否则就停止遍历
#include<iostream>
using namespace std;
int main() {
int t;
cin>>t;
while(t--) {
int n;
cin>>n;
string s;
cin>>s;
int ans=n;
for(int i=0; i<n/2; i++) {
if(s[i]!=s[n-i-1])
ans-=2;
else
break;
}
cout<<ans<<endl;
}
}
11.加训?加餐!
比签到稍微难一点点的签到题
题意:有n个人组队去饭店,每次至少两人去,给定a数组和b数组,每个人都有对应的a[i]和b[i],要求去吃饭的所有人的sum_b[i] >= sum_a[i] ,求去饭店的最多次数。
分析:显然是贪心,我们按照b[i] - a[i]给所有人排序,让值最大的人和最小的人一起匹配,值大的人肯定不能浪费,但是值小的也就是拖后腿的人可以舍弃掉,并且我们发现每次去两个人一定最优,因为如果去三个人我踢掉一个肯定更优,因此我们用排序好后用两个指针维护即可。
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
int a[N], b[N], c[N];
int x;
int n;
void solve()
{
cin >> n;
for (int i = 1; i <= n; i++)
cin >> a[i];
for (int i = 1; i <= n; i++)
cin >> b[i];
for (int i = 1; i <= n; i++)
c[i] = b[i] - a[i];
sort(c + 1, c + n + 1);
int r = n, l = 1;
int res = 0;
while (l < r)
{
if (c[l] + c[r] >= 0)
{
l++, r--;
res++;
}
else
l++;
}
cout << res << endl;
}
int main()
{
int t;
cin >> t;
while (t--)
{
solve();
}
// system("pause");
}