2020年首届算法竞赛网络挑战赛·正式赛-部分题解(赛氪)

A题 转换AV号(avtobv)

Description

最近某视频网站将使用已久的 av 视频编号升级成了新的 bv 号,新的编号扩充了编号的字符集,增加了编号的数量。

你是网站的忠实用户,这次升级迫使你将收藏的视频 av 号重新换算成 bv 号以便观看。由于原始算法比较复杂,我们将算法简化如下:

av 号以av开头,之后跟随一串数字,例如av84735341;而 bv 号以BV开头,之后跟随一串数字和字母,例如BV1i7411a794。

首先,需要将 av 号中的正整数nn异或上一个较大的正整数XX,再对其进行编码,得到编码后的串SS,最后在SS开头添加BV前缀即可。

这里所使用的编码是指把一个整数转换成一个 62 进制数,并用编码表中指定的每一位代表的符号作为该位的字符,构成编码后的串。

你的收藏中有nn个 av 号,每个 av 号都符合上面的格式。现在你已经获得了异或的正整数XX和编码的编码表(每次使用的XX和编码表可能不同),请你将 av 号转换为 bv 号并输出。

你可以参考样例解释来帮助理解上面的流程。

注:题目纯属虚构,上述算法不一定与真实情况中的编码算法相同。

Input
第一行包含两个正整数 n (1≤n≤10^4 ) 和 X (1≤X≤10^18 ),代表 av 号的数量和异或的值。第二行包含一个长为 62 的字符串 T,T​
表示编码时某一位的十进制数值为 i 时对应的编码字符。T 仅包含英文字母及数字。接下来 n 行每行包含一个字符串,分别为要转换的 av 号串,长度不超过 11。

Output
输出 n 行,每行为转换后得到的 bv 号。

Sample Input 1
5 10
0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
av1
av2
av314
av17001
av84735341

Sample Output 1
BVb
BV8
BV4U
BV4q7
BV5JxwX

题解:进制转换模板题

#include <bits/stdc++.h>
#define pb push_back
#define ll long long
#define ios ios::sync_with_stdio(false);
using namespace std;

int  t[100005], A[100005];
char str[100005], strr[100005];  
const int n = 10, m = 62;
int N;  
ll x;
char table[700];

void trans()
{
    int i, len, k;
    len = strlen(str);
    for(i=len; i>=0; --i){	//	将字母转换成十进制的数,存入int型的t数组中。 
        t[len-1-i] = str[i]-'0';
    }
    for(k=0; len;)
    {
        for(i=len; i>=1; --i)
        {
            t[i-1] += t[i]%m*n;	//	向高位进位 
            t[i] /= m;	//	本位约去进制数 
        }
        A[k++] = t[0] % m;	//	处理最高位 
        t[0] /= m;
        while(len>0 && !t[len-1]) //	去除前导0 
			--len;
    }
    strr[k] = 0;
    for(i=0; i<k; ++i)	
        strr[k-1-i] = table[A[i]];
}

int main(){
	scanf("%d%lld", &N, &x);
	scanf("%s", table);
	getchar();
	int l = strlen(table);
	for(int i = 0; i < l; ++i)
		mp[table[i]] = i;
	ll k;
	string tt;
	while(N--){
		memset(str, 0, sizeof str);
		memset(strr, 0, sizeof strr);
		scanf("av%lld", &k);
		getchar();
		k = k ^ x;
		tt = to_string(k);
		for(int i = 0; i < (int)tt.length(); ++i){
			str[i] = tt[i];
		}
		str[(int)tt.length()] = 0;
		trans();
		printf("BV%s\n", strr);
	}

	return 0;
}

G题 中位数 median

Description

小X喜欢挑战数学难题,现在有一个长度为 nn 的序列,他对于不同的数字有不同的喜爱程度。例如他可能更喜欢2333而不是3222,因为2333更朗朗上口。但出于严谨,小X为这n个数字定义了一个美观度。
对于数字a[i],能找到长度为奇数的区间[l,r], l≤i≤r,且[l,r]的中位数为a[i]。最长的满足上述要求的区间[l,r],其区间长度r-l+1即为数字a[i]的美观度。(比较大小时,数值为第一关键字,下标为第二关键字)。
中位数的“中”意为按此规则比较后位于中间位置的数,例如只有第二个2是序列{1, 2, 2, 2, 3}的中位数,而第一个2和第三个2的美观度为3。
​小X想知道某一段区间中美观度最大能是多少。于是给出 QQ个询问,每次给出一个l,r,询问区间 [l, r]内美观度的最大值是多少。

Input
第一行一个整数n。
接下来 n 个整数,代表 a[i]。
接下来给出一个整数Q,代表有 Q 个区间。
接下来Q行,每行两个整数l, r (l ≤ r),表示区间的左右端点 。

Output
对于每个区间的询问,输出一行,为答案。

题解:
这题的题意花了很久才搞懂,意思是:一个数a[i]的美观度是,a[i]所处的一个区间 [l, r] 的长度是奇数,且 a[i] 是这个区间的中位数(顺序可乱),并且满足该l,r区间是最大的。
先来看下样例:
样例的第4组询问(1,1)区间,这个数是第一个16,它的美观度是3,来自于区间 {16, 19, 7},这是最大的奇数区间(以第一个16为中位数的区间还有更长的,但长度不是奇数)。
因此问题就在于预处理求出每个 a[i] 的美观度,然后就是查找区间最值问题,用st表即可解决(ST表学习)。
难点在于预处理美观度,方法:对于每个数x,设置两个cnt,分别从x的位置向前和向后找,碰到小于x则-1,否则+1。 此处利用了抵消的效果,如果x两边的两个cnt的值相加为0,则说明x在这两个cnt对应的端点的区间内,是中位数。然后可以开两个数组,L和R,L[i]表示往左边的cnt等于 i 时,此时的下标,R[i]同理。那么,L[i], R[-i] 一定是当前数x作为中位数的一个区间。 因此对于x,求出一遍cnt后,遍历cnt的值就能找到所有x作为中位数的区间,即 [ L[i], R[i] ],加上奇数判断后就可以来更新x的美观度。

#include <bits/stdc++.h>
#define pb push_back
#define ll long long
#define ios ios::sync_with_stdio(false);
using namespace std;

const int d = 2222;	//	偏移量
int a[20010], value[20010], st[20010][1000];
int n, q;
int l[50000], r[50000];	// L[i]表示往左边的cnt等于 i 时,此时的下标

inline int search(int l, int r){
    int k = (int)(log((double)(r - l + 1)) / log(2.0));
    return max(st[l][k],st[r - (1 << k) + 1][k]);
}

inline void cal(){
	for(int i = 1; i <= n; ++i){
		memset(l, 0, sizeof l);
		memset(r, 0, sizeof r);
		l[d] = r[d] = i;	//	这个不能漏,中位数有可能处在区间的端点
		int cnt1 = 0, cnt2 = 0;	//	左、右cnt
		for(int j = i-1; j >= 1; --j){
			if(a[j] <= a[i])
				cnt1--;
			else
				cnt1++;
			l[cnt1+d] = j;
		}
		for(int j = i+1; j <= n; ++j){
			if(a[j] < a[i])
				cnt2--;
			else
				cnt2++;
			r[cnt2+d] = j;
		}
		int len;
		for(int j = -n; j <= n; ++j){
			if(!r[j+d] || !l[-j+d])
				continue;
			len = r[j+d] - l[-j+d] + 1;
			value[i] = max(value[i], len % 2? len : 0);	//	计算a[i]美观度
		}
	}
}

int main(){
	scanf("%d", &n);
	for(int i = 1; i <= n; ++i)
		scanf("%d", &a[i]);
	cal();	//	预处理美观度
	for(int i = 0; i < n; ++i)
		st[i][0] = value[i+1];
	for(int j = 1; (1 << j) <= n; ++j){
		for(int i = 0; i+(1 << j) <= n; ++i)
			st[i][j] = max(st[i][j-1], st[i+(1 << (j-1))][j-1]);
	}// st表的初始化
	scanf("%d", &q);
	int l, r;
	while(q--){
		scanf("%d%d", &l, &r);
		cout << search(l-1, r-1) << endl;
	}

	return 0;
}

(处理L、R数组时,因为cnt可能为负值,所以使用了偏移量。原先是用unordered_map感觉方便,但超时了。。。)

恰饭

Description
下课了。小 X 和班里的同学来到食堂吃饭。不幸的是,他因为腿比较短,是最后到达食堂的。此时他的前面有N名同学在排队打饭,这N名同学打饭各需要一段时间。已知食堂有M个售饭窗口。请问何时能轮到X打饭?

​ TIPS:前N名同学必须按照顺序来打饭,各自需要一个时间。而N个人按顺序排队打饭方式为:开始,前M个人依次在M个窗口打饭。当这M个窗口中有一个人完成打饭,紧接着第M+1个名同学来到这个窗口打饭,假设走路不花时间,依次执行下去。

Input
第一行两个整数N,M。分别为前面排队打饭的同学数目,和M个售饭窗口。
​接下来𝑁行每行一个整数表示每个人打饭所需要用的时间。

Output
一行一个整数表示答案。

题解: 排队问题,打饭快的人肯定先出来,想到优先队列。用一个变量记录当前已经等待的时间,每次有人出队时,新的人入队,入队的是这个人打饭所需的时间+当前时间。当窗口排不满m个人的时候,说明小X可以进入队列了。

#include <bits/stdc++.h>
#define pb push_back
#define ll long long
#define ios ios::sync_with_stdio(false);
using namespace std;

int n, m, t;
ll a[1000010];
priority_queue<ll, vector<ll>, greater<ll> > que;//队头是小的

int main(){
	ios;
	scanf("%d%d", &n, &m);
	for(int i = 0; i < n; ++i){
		scanf("%lld", &a[i]);
	}
	int p = 0;
	for(p = 0; p < m; p++)
		que.push(a[p]);
	ll now = que.top();
	while((int)que.size() == m){
		if(que.top() == now){
			ll tmp = que.top();
			while(que.top() == now && (int)que.size() == m){
				if(p < n)
					que.push(a[p++] + now);
				que.pop();
			}
			now += tmp - now;
		}
		else{
			now += que.top() - now;
			que.pop();
			if(p < n)
				que.push(a[p++] + now);
		}
	}
	cout << now << endl;
	return 0;
}

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值