NYIST & SCPC 10月份 - 月赛网络赛(同步赛)

 赛时没做出来的题莫慌,记得赛后补题就行。

签到题: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");
}

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值