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;
}