第三届“图灵杯”趣味网络邀请赛(中级) 题解

整体感觉比较简单(除了 D 题 )

A 括号匹配

时间限制:1s

空间限制:256MB

题目描述

众所周知,任何一个现代编辑器都有括号匹配功能。当你编辑代码的时候,把光标移到一个括号上,编辑器就会提示你与之对应的括号。小 F 想研究这个功能的原理。

为了简化问题,小 F 打算只研究代码中的大括号。去掉其它字符后,代码中只有 \(\{\)(左大括号)和 \(\}\)(右大括号)两种字符,总长度为 。

括号匹配的规则是:

  1. \(\{\}\) 是最简单的括号序列,其左右括号互相匹配;
  2. 如果 \(S\) 是一个括号序列,那么 \({S}\) 也是一个括号序列,其左边的左括号和右边的右括号互相匹配;
  3. 如果 \(A、B\) 都是一个括号序列,那么 \(AB\)(将 \(A\) 和 \(B\) 直接拼接)也是一个括号序列。

小 F 现在打开了这份简化后的代码,他的光标正指向第 \(p\) 个字符(从 \(1\) 开始)。小 F 希望知道与这个字符相匹配的括号是第几个字符。

小 F 研究了三天三夜,还没研究出来。无奈之下,他请你给他写一个程序,完成这个功能。

输入格式

第一行两个整数 \(n,p\),表示程序的长度和光标的位置。

第二行一个字符串 \(s\),表示小 F 的程序。

输出格式

一行一个正整数,表示与之匹配的光标的位置。

样例一

input

4 3
{{}}

output

2

样例二

input

8 4
{{}{{}}}

output

7

数据范围

本题使用子任务评测方式。你的程序能够得到一个子任务的分数当且仅当你的程序通过了所有满足该子任务限制的数据点。

对于所有数据,\(1\leq n\leq 10^5,1\leq p\leq n,|s|=n,s_i\in\{\{,\}\}\)。

思路

签到题。

\(O(n)\) 扫过去就可以了。

代码

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+5;

int n,p,top;
int stk[N],mah[N];
char s[N];

signed main(){
	scanf("%d%d",&n,&p);
	scanf("%s",s+1);
	for(int i=1;i<=n;++i){
		if(s[i]=='{')stk[++top]=i;
		else mah[i]=stk[top],mah[stk[top--]]=i;
	}
	printf("%d\n",mah[p]);
	return 0;
}

B 质数

时间限制:1s

空间限制:512MB

问题描述

输入一个正整数 \(a\),请你找出最小的质数 \(p\),使得 \(a^p\) 和 \(p^a\) 这两个正整数的(十进制)末位数字相同。如果不存在这样的质数,请输出 \(-1\)。

一组输入中包含多个询问。

输入格式

第一行一个正整数 \(T\),表示询问数量。

接下来 \(T\) 行每行一个正整数 。

输出格式

对于每组数据输出一行答案。

样例一

input

2
9
12

output

19
-1

数据范围

对于所有数据,\(1\leq T\leq 100,1\leq a\leq 10^9\)。

Subtask 1(30pts):\(a\leq 10\)。

Subtask 2(70pts):无特殊限制。

思路

暴力枚举素数即可。

最开始以为暴力过不了,考虑的是将 \(1..9\)​ 自乘模 10 的循环节(最长10)打出来(节约快速幂的 \(log\)​),并且把质数按末尾数分类,枚举末尾数。

最后发现不需要???

素数这里用的是埃氏筛,线性筛当然更好

\(O(T\times cnt)\)​, 其中 \(cnt\)​ 是 \(1e5\)​ 内素数个数,大概是 \(1e4\)​ 个左右

我们还可以考虑对 \(cnt\) 进行优化

可以发现,我们其实只需要尝试 \(50\) 以内的素数就可以了。

代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N =1e5+5;

int T,a,cnt;
int prime[N];
bool vis[N];

void getprime(){
	for(int i=2;i<N;++i){
		if(!vis[i])prime[++cnt]=i;
		for(int j=i;j<N;j+=i)vis[j]=1;
	}
}

int ksm(int a,int b){
	int res=1;
	while(b){
		if(b&1)res=res*a%10;
		b>>=1,a=a*a%10;
	}
	return res%10;
}

signed main(){
	cin.tie(0);cout.tie(0);
	getprime();cin>>T;
	while(T--){
		cin>>a;
		bool fg=0;
		for(int i=1;i<=cnt;++i) 
			if(ksm(a,prime[i])%10==ksm(prime[i],a)%10){
			cout<<prime[i]<<'\n',fg=1;
			break;
		}
		if(!fg)puts("-1");
	}
	return 0;
}

C 机器人

时间限制:2s

空间限制:512MB

问题描述

一个机器人在数轴上移动,机器人所在的坐标始终为整数(可以是负数,零,或正数)。假设机器人当前在数轴上 \(x\) 点,当收到指令 \((d,k)\) 后(其中 \(d\) 是整数,\(k\) 是正整数),机器人会移动到数轴上 \((x+d)\) 点的位置,并获得 \(|2kx+kd|\) 的分数(其中 \(|·|\) 表示绝对值)。

机器人初始时在数轴上的 \(0\) 点。现在给定了 \(n\) 个指令 \((d_1,k_1),(d_2,k_2),\ldots,(d_n,k_n)\) ,机器人需要执行其中每一个指令恰好一次,但是执行指令的先后次序还没有确定。请你合理安排执行这些指令的顺序,使得机器人获得的总分数最大化。你只需要输出最大总分即可。

输入格式

第一行一个正整数 \(n\),表示指令的数量。

接下来 \(n\) 行每行两个整数 \(d_i,k_i\)。

输出格式

输出一个非负整数,表示最大总分。

样例一

input

3
-3 2
0 1
3 2

output

18

数据范围

对于所有数据,\(1\leq n\leq 3\times 10^5,-1000\leq d_i\leq 1000,1\leq k_i\leq 1000\)。

Subtask 1(30pts):\(n\leq 18\)。

Subtask 2(70pts):无特殊限制。

思路

可以 \(O(n!)\) 暴力枚举顺序,这样有 \(30pts\)

考虑邻项交换贪心,考虑 \(cmp\) 函数的设计。

有一个显然错误的想法是:

bool cmp(Item a,Item b){
    return abs(a.d*a.k+2*a.d*b.k)+abs(b.d*b.k+2*b.d*a.k)>
           abs(b.d*b.k+2*b.d*a.k)+abs(a.d*a.k+2*a.d*b.k);
}

因为 \(x.d\times x.k\)​ 其实可以看做固定贡献,而关键影响因素是 \(a.d\times b.k\)

我们不应该让一个固定贡献影响顺序,所以,

bool cmp(Item a,Item b){
	return a.d*b.k>a.k*b.d;
}

通过排序得到操作执行顺序后,我们需要正、倒扫两遍。

\(O(n\log n)\)

代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 3e5+2;

struct Item{
	int d,k;
}item[N];
int n,as1,as2;

bool cmp(Item a,Item b){
	return a.d*b.k>a.k*b.d;
}
int mabs(int a){
	return a<0?-a:a;
}
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
	cin>>n;
	for(int i=1;i<=n;++i)cin>>item[i].d>>item[i].k;
	sort(item+1,item+n+1,cmp);
	for(int i=1,pos=0;i<=n;++i){
		as1+=mabs(2*item[i].k*pos+item[i].k*item[i].d);
		pos+=item[i].d;
	}
	for(int i=n,pos=0;i;--i){
		as2+=mabs(2*item[i].k*pos+item[i].k*item[i].d);
		pos+=item[i].d;
	}
	cout<<max(as1,as2)<<'\n';
	return 0;
}

D 欧拉回路

时间限制:

空间限制:

问题描述

对于一个整数序列 \([b_1,b_2,\ldots,b_m]\),可以定义一个 \(m\) 个点(从 \(1\) 到 \(m\) 标号)的无向图,其中对于任意两个点 \(1\leq i<j\leq m\),它们之间连一条双向边当且仅当 \(b_i<b_j\)。我们称这个序列是的,当且仅当这个无向图存在欧拉回路。欧拉回路是指一条首尾相接,且经过每条边恰好一次的路径。(注意,如果一个图包含 \(0\) 条边,也认为它有一个长度为 \(0\) 的欧拉回路。如果一个点没有边和它相邻,那么欧拉回路不需要经过这个点。)

现在输入一个整数序列 \([a_1,a_2,\ldots,a_n]\)​,它共有 \(\frac{n(n+1)}2\)​ 个连续子序列 \([a_l,a_{l+1},\ldots,a_r]\)​(其中 \(1\leq l\leq r\leq n\))。 请问其中有多少个连续子序列是好的?

输入格式

第一行一个正整数 ,表示输入序列的长度。

第二行有 \(n\) 个整数 \(a_1,a_2,\dots,a_n\) 。

输出格式

输出一行答案。

样例一

input

7
5 6 7 1 2 3 3

output

13

样例二

input

5
30 50 10 30 70

output

8

数据范围

对于所有数据,\(1\leq n\leq 8000,0\leq a_i\leq 10^9\)。

Subtask 1(10pts):\(n\leq 80\)。

Subtask 2(20pts):\(n\leq 300\)。

Subtask 3(50pts):\(n\leq 3000\)。

Subtask 4(20pts):无特殊限制。

思路

芜湖,这个是知识盲点了

暴力 \(30pts\) 走人。

上一篇:Android多线程断点续传下载原理及实现,2021阿里Android笔试总结


下一篇:Flink任务占用资源slot数与subtask个数