NEUQ-ACM预备队-week15

207 01序列

#include<bits/stdc++.h>
using namespace std;
int k;
string a;
int main()
{
	cin >> k >> a;
	if (k == 0)
	{
		a += '1';
		long long temp = 0,ans=0;
		for (int i = 0; i < a.length(); i++)
		{
			if (a[i] == '0')
			{
				temp++;
				
			}
			else
			{
				ans += temp * (temp + 1) / 2; 
				temp = 0;
			}
		}
		cout << ans;
	}
	else
	{
		//sum记录前i项和(只不过由于是01串的缘故 前i项和也就是前i项中1的个数)
		vector<int>sum(a.size() + 1);
		//Map记录含有1的个数相同的字串的长度
		map<int, int>Map;
		Map[0] = 1;
		for (int i = 0; i < a.size(); i++)
		{
			if (i == 0)
				sum[i] = a[i]-'0';
			else
				sum[i] = sum[i - 1] + a[i]-'0';

			Map[sum[i]]++;
		}
		//用index表示下界 index+k表示上界 从index~index+k这段MapKey值中
		//必然是那些含有1的个数为k的长度
		int index = 0,ans=0;
		while (Map[index + k] != 0)
		{
			ans += Map[index]*Map[index+k];
			index++;
		}
		cout << ans;
	}
	return 0;
}

307 饿饿饭饭2

#define  _CRT_SECURE_NO_WARNINGS
#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#include<bits/stdc++.h>
using namespace std;
//原题链接:http://oj.daimayuan.top/course/11/problem/561
//个人感觉本题和之前整齐的数组是一类型题 Maybe数论类型的题
//题目并不难 关键要分析清楚 两个数 做无限地乘二或乘三的操作 如何才能
//变成同一个数
//本题打眼一看 感觉是做不出来的题 这种思维之前是没有的
//后来思考了一下
//如果我们给定了一个数字a
//若a可以表示为 a=2^n*3^m*x(其中,n,m为非负整数,x为任意正整数)
//即一个基础元x与若干个2及3相乘的形式
//若基础元相同 则显然 在任意乘2/乘3的条件中 数字就是可以相互转换的
//基于这个猜测 利用set去重的性质 让数字a不断地做整除2整除3的操作
//直到无法整除2与3 所剩的就是基础元x 将x放入set中
//最后判断set长度是否为一即可
int T, n;
int main()
{
	scanf("%d", &T);
	while (T--)
	{
		scanf("%d", &n);
		vector<int>v(n + 1);
		for (int i = 1; i <= n; i++)
			scanf("%d", &v[i]);
		set<int> ans;
		for (int i = 1; i <= n; i++)
		{
			while (v[i] % 2 == 0 || v[i] % 3 == 0)
			{
				if (v[i] % 2 == 0)
					v[i] /= 2;
				else if (v[i] % 3 == 0)
					v[i] /= 3;
			}
			ans.insert(v[i]);
		}
		if (ans.size() != 1)
			printf("NO\n");
		else
			printf("YES\n");
	}
	return 0;
}

401 字串分值和

在这里插入图片描述

#define  _CRT_SECURE_NO_WARNINGS
#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#include<bits/stdc++.h>
using namespace std;
//原题链接:http://oj.daimayuan.top/problem/563
// 
//1.每个字母只有在第一次出现时才有贡献度,因此可以统计每个字母在第一次出现的情况下,能被多少子串所包含;
//2.用 last[s[i]] 记录字母 s[i] 上一次出现的位置;
//3.那么往左最多能延伸到 last[s[i]] + 1,其到第 i 个字母一共有 i - last[s[i]] 个字母;
//4.同理往右最多能延伸到 n,其到第 i 个字母一共有 n - i + 1 个字母;
//5.二者相乘,就是该字母被不同子串所包含的总次数;
unsigned long long ans;
string s;
int last[26];

int main()
{
	cin >> s;
	int n = s.size();
	s = ' ' + s;

	for (int i = 1; i <= n; i++)
	{
		ans += (i - last[s[i] - 97]) * (n - i + 1);
		last[s[i] - 97] = i;
	}
	cout << ans;

	return 0;
}

402 蒟蒻

利用map.count()以及map自动为Key排序的功能

#define  _CRT_SECURE_NO_WARNINGS
#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#include<bits/stdc++.h>
using namespace std;
int n;
map<int, int>price, flavor;
int op, w, t;
int ans;
//题目链接:http://oj.daimayuan.top/course/11/problem/605
//map在本题的特别应用有三
//1	map内部元素(基本数据类型)自动依Key的字典序排序
//2	map的Key只能在map中出现一次 利用count(Key) 实现了查询该Key值是否在map中存在的功能
//3 map的指针运用
//	map->frist  返回Key
//	map->second 返回Value
int main()
{
	cin >> n;
	while (n--)
	{
		scanf("%d", &op);
		if (op == 1)
		{
			scanf("%d%d", &w, &t);
			if (price.count(w) || flavor.count(t))continue;
			price[w] = t;
			flavor[t] = w;
		}
		else if (op == 2)
		{
			//这步有点优美 利用一个map对键的排序 迅速查找在另一个map中的对应元素
			flavor.erase(price.begin()->second);
			price.erase(price.begin());
		}
		else
		{
			price.erase(flavor.begin()->second);
			flavor.erase(flavor.begin());
		}
	}
	for (auto it = flavor.begin(); it != flavor.end(); it++)
	{
		ans += it->second;
	}
	printf("%d", ans);

	return 0;
}

403 锦标赛

(感觉本题是一道模拟题hahah)

在这里 我想给定一个名词 我把它称为“屹立不倒的玩家”

屹立不倒的玩家指那些:该玩家能力值比大于自己能力值的最小能力值玩家能力值相差不超过K(即此人不会在与离自己最近的高能力值的玩家对战中百分百失败) 同时 该玩家能力值比小于自己能力者的最大能力值玩家能力值相差超过K(即此人在与离自己最近的低能力值的玩家对战中百分百成功)

由此 在解决本题时 我们只需要对玩家的能力值排序 从大到小的判断哪一位玩家是 能力值最小的最后屹立不倒者

判断方式是:(假定玩家能力值从大到小排序分别为 a,b,c,d,e)

对玩家a 当a-b>K时 那么a本人绝无失败的可能 获胜者只有a a就是能力值最小的屹立不倒者

若a-b<=K 则a不一定屹立不倒 会在与b的对战中失败 那么a就不是能力值最小的屹立不倒者

之后判断b 是同理的 到达最后一个人的轮次中则不用判断(具体原因见代码)

#define  _CRT_SECURE_NO_WARNINGS
#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#include<bits/stdc++.h>
using namespace std;
int n, K;
bool cmp(int a, int b)
{
	return a > b;
}
int ans;
int main()
{
	scanf("%d%d", &n, &K);
	vector<int> v(n);
	for (int i = 0; i < n; i++)
	{
		scanf("%d", &v[i]);
	}
	sort(v.begin(), v.end(), cmp);
	for (int i = 0; i < n; i++)
	{
		ans++;
		//如果已经进入到最后一个人的轮次了 那就不用管了 他已经存活
		//那么他本人就是最小能力值的最后屹立不倒者 在他之前的人都可能获胜
		//如果不是最后一个人的话 就需要比较他和他下一个人 如果他下一个人
		//绝无可能战胜他的话 那么他在此就必然屹立不倒 游戏已经变成了此人
		//和此人之前的人的游戏
		if (i!=n-1&&v[i] - v[i + 1] > K)
			break;
	}
	cout << ans;
	return 0;
}

404 可重排列

个人感觉这个是对全排列的一点简单变式 熟悉全排列的写法后 本题应该手到擒来

#include<bits/stdc++.h>
using namespace std;
int a[10], check[10];
int n,cnt;
int arr[100];
int len;
//本题是十分经典的DFS题--全排列问题的一点简单变式
//只要能轻松解决全排列问题的 本题在代码上也不会有难度
void dfs()
{
	if (len == cnt)
	{
		for (int i = 1; i <= len; i++)
			printf("%d ", arr[i]);
		printf("\n");
		return;
	}
	for (int i = 1; i <= n; i++)
	{
		if (check[i] == a[i])continue;
		arr[++len] = i;
		check[i]++;
		dfs();
		check[i]--;
		len--;
	}
}

int main()
{
	cin >> n;
	for (int i = 1; i <= n; i++)
	{
		cin >> a[i];
		cnt += a[i];
	}
	dfs();
	return 0;
}

405 进制转换

用基本的进制转化方法就好了 通过打表 将061依次与’0‘’9‘ ’A’~‘Z’ ‘a’~'z’对应就可以了

#include<bits/stdc++.h>
using namespace std;
int n,m;
unsigned long long ans;
unsigned long long res;
int p;
string temp;
int hash1[200],hash2[200];
//像是常规的进制转化题?打表将数据一一对应就好了
inline void init()
{
	char c = 'A';
	int cnt = 10;
	while (c <= 'Z')
	{
		hash1[c] = cnt;
		hash2[cnt++] = c++;
	}
	c = 'a';
	while (c <= 'z')
	{
		hash1[c] = cnt;
		hash2[cnt++] = c++;
	}
	c = '0', cnt = 0;
	while (c <= '9')
	{
		hash1[c] = cnt;
		hash2[cnt++] = c++;
	}
}
int main()
{
	
	init();
	cin >> n >> m;
	
	while (n--)
	{
		res = 0;
		cin >> p >> temp;
		for (int i = 0; i < temp.size(); i++)
		{
			res = res * p + hash1[temp[i]];
		}
		ans += res;
	}
	string end1;
	while (ans != 0)
	{
		unsigned long long temp = ans % m;
		end1 += hash2[temp];
		ans /= m;
	}
	for (auto i = end1.rbegin(); i != end1.rend(); i++)
		cout << *i;
	return 0;
}

406 循环子串

#define  _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
using namespace std;
//原题链接:http://oj.daimayuan.top/course/11/problem/552
//首先我们思考对字符串进行移位操作等价于何?显然 等价于在字符串后
//额外拷贝一个字符串 之后利用find()查找子串是否存在即可
int t;

int main()
{
	cin >> t;
	while (t--)
	{
		int n;
		scanf("%d", &n);
		string s;
		cin >> s;
		s += s;
		bool flag = true;
		for (int i = 2; i <= n; i++)
		{
			for (int j = 0; j + i <= n; j++)
			{
				string now = s.substr(i, j);
				reverse(now.begin(), now.end());
				if (s.find(now) == s.npos)
				{
					flag = false;
					printf("NO\n");
					break;
				}
			}
			if (!flag)
				break;
		}
		if (flag)
			printf("YES\n");
	}
	
	
	
	

	return 0;
}

407 饿饿 饭饭之暑假大狂欢

很惊讶这样的想法居然AC了

判断一个人能不能赢的思路已经写在了主体代码前 重点就是判断此人持有的数字序列是否存在他人的数字序列是此人的子序列

但是这里判断是否为子序列的方法有点暴力 而且map这个STL用起来也怪怪的 只是用了映射来降低时间复杂度 而Map->second完全没有利用起来 随便写都可以

所以应该可以很大程度的优化 之后去读读佬们的代码(hiahia)

#define  _CRT_SECURE_NO_WARNINGS
#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#include<bits/stdc++.h>
using namespace std;
//原题链接:http://oj.daimayuan.top/course/11/problem/615
//研究一下什么叫做对某人最利的情况:显然是裁判念出的每一个数字都是
//此人持有的数字之一是对此人最有利的情况
//在这种情况下 此人怎么样会输呢?
//就是除了此人以外 有某人持有的数字是此人数字序列的子序列
//因此 对于每个人是否能获胜 只要看有没有人是此人的子序列就好了
//如果有的话 那么此人必然不可以获胜 就是去找含有某些数字的最小序列
//这里暂时的思路是使用map映射 看起来数据也不是很大 应该可以过
//而且map的Value值不重要 只要知道Key在不在就好了
map<int, int>Map[100];
int t, n;
int cnt;
int main()
{
	scanf("%d", &t);
	for(int i=0;i<t;i++)
	{
		scanf("%d", &n);
		for (int i = 0; i < n; i++)
		{
			int temp;
			scanf("%d", &temp);
			Map[cnt][temp] = temp;
		}
		cnt++;
	}
	for (int i = 0; i < t; i++)
	{
		//ans 检查全部的数字序列中 是否有任意一个序列是i的子序列
		bool ans = false;
		for (int j = 0; j < t; j++)
		{
			if (i == j)continue;
			//flag 检查j是否为i的子序列
			bool flag = true;
			for (auto k = Map[j].begin(); k != Map[j].end(); k++)
			{
				//如果j中存在的键值在i中不存在 那么j不是i的子序列
				if (!Map[i].count(k->first))
				{
					flag = false;
					break;
				}
			}
			//如果flag是true的话 说明j是i的子序列
			if (flag)
			{
				ans = true;
				break;
			}
			//如果flag是false的话 继续找下一个子序列
		}
		if (ans)
			printf("NO\n");
		else
			printf("YES\n");
	}
	return 0;
}

503 A-B数对

这道题主要是想办法降低时间复杂度吧!

#define  _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
using namespace std;
//原题链接:http://oj.daimayuan.top/course/11/problem/616
//由于c是正整数 所以基本思路就是从大到小遍历
//由于题目说明 不同位置 相同数字的数算不同的数字 要重复计算
//那么 若使用基本的两层遍历
//题目2e5的数据 O(N^2)的时间复杂度肯定会爆
//那么考虑优化
//1、二分是必然的
//2、无法用set数组去重 因为数字重复也算多个数字
//由此 这里思考 用Map去存储 因为一个数字的位置和自己的值想必是无关的
//如果这个a-b=c合法 那么我们只需要用|a|*|b|即ab的个数积即可
//ans += i->second * j->second;
//
//此外用到了lower_bound() 能调用的绝不自己写二分
int n, c;
int ans;
map<int, int>Map;
int main()
{
	scanf("%d%d", &n, &c);
	for (int i = 0; i < n; i++)
	{
		int temp;
		scanf("%d", &temp);
		if (Map.count(temp))
			Map[temp]++;
		else
			Map[temp] = 1;
	}
	for (auto i=Map.rbegin(); i!=Map.rend() ; i++)
	{
		//lower_bound(begin, end, num)
		auto j = Map.lower_bound(i->first-c);
		for (j;j!=Map.end(); j++)
		{
			if (i->first - j->first == c)
			{
				ans += i->second * j->second;
			}	
			else
				break;
		}
	}
	printf("%d", ans);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值