该题解为JMU-ACMer提供。让我们为善良的出题人点赞。谢谢!!!!
题解顺序可能跟比赛中不太一样,请通过标题进行查找
文章目录
一、OrzLJL进行到底
这题大家应该是很熟悉了。毕竟每次校选都必膜一次。
需要注意的地方就是输出\
的时候需要转移一下。(忘记转义符的,c语言可以回炉重造一下)。
代码:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
char matrix[7][100] = {
"\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\",
"\\\\ _ _ _ __ ____ ______ ____ \\\\",
"\\\\ | | | | | \\ \\ / /\\ \\ / / _ \\/ ___| \\\\",
"\\\\ | | _ | | | \\ V / \\ V /| | | \\___ \\ \\\\",
"\\\\ | |__| |_| | |___ | | | | | |_| |___) | \\\\",
"\\\\ |_____\\___/|_____| |_| |_| |____/|____/ \\\\",
"\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\"
};
int main() {
for (int i = 0; i < 7; i++) {
printf("%s\n", matrix[i]);
}
return 0;
}
二、邪恶的TXT
这道就是个阅读理解题。需要根据这段英文写出这段答案。
首先The first three characters are "104"
. 所以前三个字符就是 104 了。
之后The next three characters are the lowercase of "TXT"
接下去三个字母是小写的txt。
最后the last fifteen characters are reversed with the string "neredeeixiuzihs"
最后十五个字符只需要把最后那个字符串倒转过来就是了。shizuixieederen
代码:
#include<stdio.h>
int main(){
printf("104txtshizuixieederen");
return 0;
}
三、H=1/2gt2
这题就是个高中物理。以下是某两位大佬考完说过的话。
-
符号定义
h0:桌子的高度
h1:纸板的高度
x:纸板到桌子的水平距离
y:衣服到桌子的水平距离
-
考虑一种情况:h0<h1
纸板高于桌子,永远无法到达。
-
第二种情况: h0 >h1
-
桌子高于纸板,小球做平抛运动,此时弹珠能到达地面的最近距离和纸板之间形成了一个盲区,
此处是无法到达的,输出 xie’e
-
代码:
#include <algorithm>
#include <cassert>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
int main() {
double x, h0, h1;
double y;
cin >> h0 >> h1 >> x >> y;
assert(h0 > 0 && h1 > 0 && x > 0 && y > 0);
double t = sqrt(2.0 * h0 / 10.0);
double dh = h0 - h1;
if (dh < 0 && y > x) cout << "xie'e";
else {
double t1 = sqrt(2 * dh / 10);
double vx = x / t1;
double t2 = sqrt(2 * h0 / 10);
double right_bound = t2 * vx;
if (y > x && y < right_bound) cout << "xie'e";
else {
double v = y / t2;
printf("%.2f", v);
}
}
return 0;
}
四、勋总的课1
这题是要输出整数的二进制形式。如果你学过源码、补码、位运算。那么这题是很容易做出来的。如果还未听过或者学习过的话。建议先去学下。
#include<iostream>
using namespace std;
//-------------------------------------
int main(){
for(int n; cin>>n; ){
for(int i=31; i>=0; --i)
{
cout<<(n>>i & 1);
if(i%8==0 && i!=31) cout<<' ';
}
cout<<"<->"<<n<<"\n";
}
}
五、谁是龙王
要找出谁的发言次数最多。如果发言次数最多的有多个人,那么就选则最先发言的人为龙王。
这题如果会STL的话写起来是很轻松的。用过map记录每个人的发言数,还有一个map记录每个人第一次发言时间点。 然后遍历map找出发言数最多的。接着找出发言次数最多的人中,第一次出现的。map的插入和查找时间都是logn的,所以用这种方法整体时间复杂度为O(nlogn)。
纯C写发的话,可以定义一个结构体,这个结构体里面存放一个字符串,用于保存名字,还有一个idx遍历用于保存第一次出现的位置,count遍历用于保存出现的次数。
之后定义一个该结构体数组,每次输入一个人名后,先遍历数组,判断当前输入的人名是否在数组中了,如果有,则出现次数+1。若没有,则加到数组中。
因为题面保证人名的种类不超过200个,发言条数n不过超1e5。所以整体复杂度为O( 200n)。也才1e7。不会发生TLE。
struct node{
char name[100];
int count,idx;
}
代码:
#include <bits/stdc++.h>
using namespace std;
int main() {
//freopen("../tianti/2.in", "r", stdin);
// freopen("../tianti/2.out", "w", stdout);
int n;
cin >> n;
string a;
int i;
unordered_map<string, int> mmid, map;
for (i = 0; i < n; i++) {
cin >> a;
mmid[a]++;
if (!map[a]) map[a] = i + 1;
}
int maxn = 0;
string name = "";
for (auto &val : mmid) {
maxn = max(maxn, val.second);
}
int idx = n + 1;
for (auto &val : map) {
if (maxn == mmid[val.first]) {
if (idx > val.second) {
idx = val.second;
name = val.first;
}
}
}
cout << name << endl;
return 0;
}
六、暴风雨前的宁静
对于n<=3的情况,我们不难特判枚举出来,当n>3时,我们可以按照先把1-n中的偶数输出,然后再输出1-n中的奇数。n为奇数时:2,4,6,…n-1,1,3,5,…,n,因为除了n和2这个位置其他位置的相邻奇偶性相同,必然满足差值>1,因为n>3且n为奇数——即n>=5,所以n-2>1的;n为奇数时:2,4,6,…n-1,1,3,5,…,n,同理可证其满足题目要求。
#include <stdio.h>
int main()
{
int h, i, j, n;
scanf("%d", &n);
if(n==1||n==2)
{
printf("1\n1");
}
else if(n==3)
{
printf("2\n1 3");
}
else if(n==4)
{
printf("4\n2 4 1 3");
}
else
{
printf("%d\n1", n);
for(i=3;i<=n;i+=2)
{
printf(" %d", i);
}
for(i=2;i<=n;i+=2)
{
printf(" %d", i);
}
}
return 0;
}
七、背靠背
这题就是找规律输出了。
通过观察可以看到,就单独一行来看,中间被一个空格分成左右两边,而且是堆成的,但末尾是没有空格的。
然后我们先单单只看左边,每行字符串的个数是逐渐加1的。前导空格是逐渐减一的。
还有注意多组输入之间读入空白字符的问题。代码如下
#include<stdio.h>
int main()
{
char ch;
int i, n, j, temp,h,k;
while (~scanf(" %c %d", &ch, &n))
{
temp = n;
for (i = 1; i <= n; i++)//控制行数
{
for (j = 1; j < temp; j++)//每行空格数
printf(" ");
for (j = 0; j < i; j++)
printf("%c", ch);
printf(" ");
for (j = 0; j < i; j++)
printf("%c", ch);
printf("\n");
temp--;
}
}
return 0;
}
八、Nobody knows code better than me
这题是道模拟题,只要你会任意一种排序算法,就能解出来。
首先得记录每个人的编号,还有成绩,然后我们以成绩为关键字,按照从大到小的顺序排个序。
排好序之后,就可以给每个人排名了。题面要求如果有多个成绩相同的人,他们的排名是一样的,但是后面的人的排名计数依然会增加
就比如说分数为99 99 98
,那么这三个分数对应的排名就位 1 1 3
所以根据上述规则,在拍好序后给每个人排名。在拍完名之后,再以编号为关键字,从小到大排序一次。
具体实现代码如下:
/**
* Created by jiangxiaoju on 2020/10/19 20:50.
*/
#include <bits/stdc++.h>
using namespace std;
#define mp(a, b) make_pair(a, b)
#define mid ((l + r) >> 1)
#define lson rt << 1, l, mid
#define rson rt << 1 | 1, mid + 1, r
typedef long long ll;
typedef pair<ll, int> pli;
typedef pair<int, int> pii;
typedef pair<int, double> pid;
typedef pair<double, double> pdd;
typedef pair<double, int> pdi;
typedef pair<ll, double> pld;
typedef pair<double, ll> pdl;
typedef pair<ll, ll> pll;
const long long INF = 1e15;
const int maxn = 1e5 + 10;
const int mod = 1e9 + 7;
template<class T>
bool isPrime(T x) {
if (x <= 1)
return false;
if (x == 2)
return true;
if (x % 2 == 0)
return false;
T m = sqrt(x);
for (T i = 3; i <= m; i += 2) {
if (x % i == 0)
return false;
}
return true;
}
template<class T>
T gcd(T m, T n) {
return m % n == 0 ? n : gcd(n, m % n); }
ll qpow(ll x, ll y) {
ll res = 1;
while (y) {
if (y & 1)
res = res * x;
x = x * x;
y >>= 1;
}
return res;
}
template<class T>
ll qpow(ll x, ll y, T mod) {
ll res = 1;
while (y) {
if (y & 1)
res = res * x % mod;
x = x * x % mod;
y >>= 1;
}
return res;
}
struct node {
ll scores, idx, num;
node(ll a, ll b, ll c = 0) : scores(a), num(b), idx(c) {
}
};
int main() {
ios::sync_with_stdio(false);
int n;
cin >> n;
ll scores, num;
int i;
vector<node> v;
for (i = 0; i < n; i++) {
cin >> num >> scores;
v.push_back(node(scores, num));
}
sort(v.begin(), v.end(), [&](const node &n1, const node &n2) {
return n1.scores > n2.scores;
});
ll cnt = 1;
for (i = 0; i < n; i++) {
if (i == 0) {
v[i].idx = cnt;
} else {
if (v[i].scores != v[i - 1].scores) {
v[i].idx = ++cnt;
} else {
int tmp = 0;
while (i < n && v[i].scores == v[i - 1].scores) {
v[i].idx = cnt;
i++;
tmp++;
}
i--;
cnt += tmp;
}
}
}
sort(v.begin(), v.end(), [&](const node &n1, const node &n2) {
return n1.num < n2.num;
});
for(auto&val:v){
cout<<val.idx<<endl;
}
return 0;
}
如果觉得上面这种写法太麻烦,还有个跟简洁的做法。
因为n<=5000,所以n^2 能过,不需要node、pair之类的排序,因为编号是n的一个排列,所以也不需要map、离散化处理过大的情况,直接数组存储即可。这里只给出最简便的做法:O(n^2),记ans[i]为第i个人排名初始化为1,先把每一个编号的分数按下标存储进对应的数组中,然后先遍历数组枚举每一个人的分数ai,对于枚举的a,再遍历数组枚举每一个人的分数aj,如果aj>ai,就说明i这个人的排名要后退一位,因为j的成绩比他高,所以ans[i]++;最后输出按序输出ans即可。
#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
#define ll long long
#define pii pair<int,int>
#define pll pair<ll,ll>
#define mp make_pair
#define pb push_back
#define G 6.67430*1e-11
#define rd read()
#define pi 3.1415926535
using namespace std;
const ll mod = 998244353;
const int MAX1 = 100005;
const int MAX2 = 300005;
inline ll read() {
ll x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch>'9') {
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
ll fpow(ll a, ll b)
{
ll ans = 1;
while (b)
{
if (b & 1)ans = ans * a % mod;
b >>= 1;
a = a * a % mod;
}
return ans;
}
ll gcd(ll a, ll b) {
return !b ? a : gcd(b, a % b); }
ll b[10000];
ll ans[10000];
int main()
{
int n = rd;
int i;
for (i = 0; i < n; i++)
{
ll t = rd;
b[t] = rd;
ans[t] = 1;//初始化每个人的分数
}
for (i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
if (b[j] > b[i])ans[i]++;
}
}
for (i = 1; i <= n; i++)cout << ans[i] << endl;
}
九、从零开始的良心出题人招募
题目要求各个位数的平方和要=其对n取模的结果,所以可以知道每个位数都是要<=sqrt(n-1),因为n<=25,所以sqrt(n-1)<=4,因此每一位数都<=4,所以dfs搜索m个数(第一位数从1到4,之后每一位数从0到4,因为不能有前导零),复杂度O(4^m)。当然也可以直接暴力搜索m个数(第一位数从1到9,之后每一位数从0到9),然后剪枝一下——当当前的各个位数的平方和>=n的时候就直接return即可。
#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
#define ll long long
#define pii pair<int,int>
#define pll pair<ll,ll>
#define mp make_pair
#define pb push_back
#define G 6.67430*1e-11
#define rd read()
#define pi 3.1415926535
using namespace std;
const ll mod = 998244353;
const int MAX1 = 100005;
const int MAX2 = 300005;
inline ll read() {
ll x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch>'9') {
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
ll fpow(ll a, ll b)
{
ll ans = 1;
while (b)
{
if (b & 1)ans = ans * a % mod;
b >>= 1;
a = a * a % mod;
}
return ans;
}
ll gcd(ll a, ll b) {
return !b ? a : gcd(b, a % b); }
ll ans = 0;
int n;
int m;
ll k;
int f;
void dfs(int i, int sum, ll num)
{
if (i == m)
{
if (num % n == sum)
{
cout << num % k << endl;
f = 1;
}
return;
}
if (i)
{
for (int j = 0; j <= sqrt(n - 1); j++)
{
dfs(i + 1, sum + j * j, num * 10 + j);
}
}
else
{
for (int j = 1; j <= sqrt(n - 1); j++)
{
dfs(i + 1, sum + j * j, num * 10 + j);
}
}
}
int main()
{
m = rd, n = rd, k = rd;
dfs(0, 0, 0);
if (!f)cout << -1;
return 0;
}
十、勋总的课2
给一个表达式,要要判该表达式是否满足r进制下的运算结果,题面保证2<=r<=16。并且表达式中的数的值为非负数。这题最简单的方法就是用Java写了。
先介绍用Java的写法:按运算符更好完字符串后,暴力判断r从2到16的情况。因为数据可能超过2的31次方。所以用long类型的保存每个数。 在把字符串转成数值类型是,可以用Long提供的方法Long.valueOf(strings[i], radix);
记得try cathe一下异常。具体实现可以参考第一份代码。
c\c++做法,其实也跟Java的写法差不多。也是先分割字符串。然后把字符串转成数值后进行判断,只不过比较麻烦的是得自己实现字符串转成R进制下的整数的。 具体实现过程可以参考第二份代码。
Java:
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.*;
import java.util.concurrent.ArrayBlockingQueue;
public class Main {
public static Long[] change(String[] strings, int radix) {
Long[] arr = new Long[strings.length];
for (int i = 0; i < strings.length; i++) {
arr[i] = Long.valueOf(strings[i], radix);
}
return arr;
}
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
String str;
while (in.hasNext()) {
str = in.nextLine();
String[] split = str.split("[+|*|/|=|-]");
List<String> ops = new ArrayList<>();
for (int i = 0; i < str.length(); i++) {
if (str.charAt(i) == '+' || str.charAt(i) == '-' || str.charAt(i) == '*' || str.charAt(i) == '/') {
ops.add(str.charAt(i) + "");
}
}
int r;
boolean flag = false;
for (r = 2; r <= 16; r++) {
if (flag) break;
Long[] arr = new Long[20];
try {
arr = change(split, r);
} catch (Exception e) {
continue;
}
Long ans = arr[0];
int j = 0;
boolean tmp = true;
for (int i = 1; i < arr.length - 1; i++) {
Long num = arr[i];
String op = ops.get(j++);
if (op.equals("+")) {
ans += num;
} else if (op.equals("-")) {
ans -= num;
} else if (op.equals("/")) {
if (ans % num != 0) {
tmp = false;
break;
}
ans /= num;
} else if (op.equals("*")) {
ans *= num;
}
}
if (tmp && ans.equals(arr[arr.length - 1])) {
flag = true;
break;
}
}
if (flag) {
System.out.println(r);
} else {
System.out.println(-1);
}
}
}
}
c++:
#include<cstdio>
#include<string>
#include<iostream>
using namespace std;
enum op_t{
EMPTY, ADD, DEC, MUL, DIV};
int get(char ch) {
int number = -1;
if (ch >= '0' && ch <= '9') number = ch - '0';
else if (ch >= 'a' && ch <= 'z') number = 10 + ch - 'a';
else if (ch >= 'A' && ch <= 'Z') number = 10 + ch - 'A';
return number;
}
unsigned long long get(const string& str, int radix) {
unsigned long long p = 1;
unsigned long long res = 0;
for (int i = str.size() - 1; i >= 0; i--) {
int n = get(str[i]);
if (n >= radix) return -1;
res += n * p;
p *= radix;
}
return res;
}
bool check(string& s1, op_t op, string& s2, string& ans, int radix) {
unsigned long long _n1 = 0, _n2 = 0, _n3 = 0;
_n1 = get(s1, radix);
_n2 = get(s2, radix);
_n3 = get(ans, radix);
if (_n1 == -1 || _n2 == -1 || _n3 == -1) return false;
switch (op) {
case ADD:
return _n1 + _n2 == _n3;
case DEC:
return _n1 - _n2 == _n3;
case MUL:
return _n1 * _n2 == _n3;
case DIV:
return _n2 * _n3 == _n1;
default:
return false;
}
}
int main() {
string str;
while (cin >> str) {
op_t op = EMPTY;
string s1, s2, ans;
int op_idx = -1;
int eq_idx = -1;
int idx = 0;
for (char ch : str) {
int number = -1;
if (ch >= '0' && ch <= '9') number = ch - '0';
else if (ch >= 'a' && ch <= 'z') number = 10 + ch - 'a';
else if (ch >= 'A' && ch <= 'Z') number = 10 + ch - 'A';
else if (ch == '+') {
op = ADD;
op_idx = idx;
}
else if (ch == '-') {
op = DEC;
op_idx = idx;
}
else if (ch == '*') {
op = MUL;
op_idx = idx;
}
else if (ch == '/') {
op = DIV;
op_idx = idx;
}
else if (ch == '=') {
eq_idx = idx;
}
idx++;
}
s1 = string(str.begin(), str.begin() + op_idx);
s2 = string(str.begin() + op_idx + 1, str.begin() + eq_idx);
ans = string(str.begin() + eq_idx + 1, str.end());
//cout << s1 << ' ' << s2 << ' ' << ans << endl;
int radix;
for (radix = 2; radix <= 16; radix++) {
if (check(s1, op, s2, ans, radix)) {
printf("%d\n", radix);
break;
}
}
if (radix >= 17) printf("-1\n");
}
return 0;
}
十一、SICP熵
这题题意是要求一个序列中逆序对的个数。
设 A 为一个有 n 个数字的有序集 (n>1),其中所有数字各不相同。
如果存在正整数 i, j 使得 1 ≤ i < j ≤ n 而且 A[i] > A[j],则 <A[i], A[j]> 这个有序对称为 A 的一个逆序对,也称作逆序数。
求逆序对的个数做法有很多,权值线段树、树状数组、归并排序都是可以的。
权值线段树的做法,首先得学过线段树 :POJ2299 Ultra-QuickSort 权值线段树求逆序对 详细图解,用全职线段树求解的方法,可以参考这篇博客,附有详细的图解过程。
这里说下最简单的归并排序的做法,只需要在归并排序的Merge函数加句话即可。ans += (midIndex + 1 - i);
因为归并左右两个数组是已经排序好的,这时候第j个数要进来,说明左半部分(start到mid)部分中剩余的个数都是比第j个数大的,但是他们又在j前面,所以ans+=左半部分(start到mid)部分中剩余的个数。
#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
#define ll long long
#define pii pair<int,int>
#define pll pair<ll,ll>
#define mp make_pair
#define pb push_back
#define G 6.67430*1e-11
#define rd read()
#define pi 3.1415926535
using namespace std;
const ll mod = 998244353;
const int MAX1 = 100005;
const int MAX2 = 300005;
inline ll read() {
ll x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch>'9') {
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
ll fpow(ll a, ll b)
{
ll ans = 1;
while (b)
{
if (b & 1)ans = ans * a % mod;
b >>= 1;
a = a * a % mod;
}
return ans;
}
ll gcd(ll a, ll b) {
return !b ? a : gcd(b, a % b); }
long long ans = 0;
void Merge(int sourceArr[], int tempArr[], int startIndex, int midIndex, int endIndex)
{
int i = startIndex, j = midIndex + 1, k = startIndex;
while (i != midIndex + 1 && j != endIndex + 1)
{
if (sourceArr[i] > sourceArr[j])
{
tempArr[k++] = sourceArr[j++];
//cout <<i<<' '<<j<<' '<< startIndex<<' '<< midIndex<<' '<< endIndex<<' '<<(midIndex + 1 - i) << endl;
ans += (midIndex + 1 - i);
}
else
tempArr[k++] = sourceArr[i++];
}
while (i != midIndex + 1)
tempArr[k++] = sourceArr[i++];
while (j != endIndex + 1)
tempArr[k++] = sourceArr[j++];
for (i = startIndex; i <= endIndex; i++)
sourceArr[i] = tempArr[i];
}
void MergeSort(int sourceArr[], int tempArr[], int startIndex, int endIndex)
{
ll midIndex;
if (startIndex < endIndex)
{
midIndex = (startIndex + endIndex) / 2;
MergeSort(sourceArr, tempArr, startIndex, midIndex);
MergeSort(sourceArr, tempArr, midIndex + 1, endIndex);
Merge(sourceArr, tempArr, startIndex, midIndex, endIndex);
}
}
int a[1000005], b[1000005];
int main()
{
int i;
int n = rd;
for (int i = 0; i < n; i++)a[i] = rd;
MergeSort(a, b, 0, n - 1);
cout << ans << endl;
return 0;
}
十二、汤氏集团
这题题意是要求无序数组内,任意区间的第k大数的模板题。
可以用可持久化线段树、划分树去求解。由于这两部分内容太多,就请大家自己网上搜教程了吧。
这里附上可持久化线段树的代码。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 10;
struct Node {
int l, r, sum;
} T[maxn * 40];
int root[maxn], arr[maxn], n, m, cnt = 0;
vector<int> v;
int getid(int x) {
return lower_bound(v.begin(), v.end(), x) - v.begin() + 1;
}
void update(int l, int r, int &x, int y, int pos) {
T[++cnt] = T[y];
x = cnt;
T[x].sum++;
if (l == r) return;
int mid = (l + r) >> 1;
if (pos <= mid) update(l, mid, T[x].l, T[x].l, pos);
else
update(mid + 1, r, T[x].r, T[x].r, pos);
}
int query(int l, int r, int x, int y, int k) {
if (l == r) return l;
int sum = T[T[y].r].sum - T[T[x].r].sum;
int mid = (l + r) >> 1;
if (sum >= k) return query(mid + 1, r, T[x].r, T[y].r, k);
else
return query(l, mid, T[x].l, T[y].l, k - sum);
}
int main() {
ios::sync_with_stdio(false);
cin >> n >> m;
int i;
for (i = 1; i <= n; i++) {
cin >> arr[i];
v.push_back(arr[i]);
}
sort(v.begin(), v.end());
v.erase(unique(v.begin(), v.end()), v.end());
for (i = 1; i <= n; i++) {
update(1, n, root[i], root[i - 1], getid(arr[i]));
}
int a, b, k;
for (i = 1; i <= m; i++) {
cin >> a >> b >> k;
cout << v[query(1, n, root[a - 1], root[b], k) - 1] << endl;
}
return 0;
}
十三、TXT斩向日葵
#include<cstdio>
#include<algorithm>
using namespace std;
const int N = 100010, M = 200010;
int Case, n, m, i, j, x, y, a[N], q[N], g[N], v[M << 1], nxt[M << 1], ed, f[N], fa[N], wake[N]; long long ans;
inline void add(int x, int y) {
v[++ed] = y; nxt[ed] = g[x]; g[x] = ed; }
inline bool cmp(int x, int y) {
return a[x] > a[y]; }
int F(int x) {
return f[x] == x ? x : f[x] = F(f[x]); }
int main() {
scanf("%d%d", &n, &m);
for (ed = 0, i = 1; i <= n; i++) {
scanf("%d", &a[i]);
q[i] = f[i] = i;
g[i] = fa[i] = wake[i] = 0;
}
while (m--)scanf("%d%d", &x, &y), add(x, y), add(y, x);
sort(q + 1, q + n + 1, cmp);
for (i = 1; i <= n; i++) {
x = q[i];
wake[x] = 1;
for (j = g[x]; j; j = nxt[j]) {
y = v[j];
if (!wake[y])continue;
y = F(y);
if (y == x)continue;
fa[y] = f[y] = x;
}
}
for (ans = 0, i = 1; i <= n; i++)ans += a[i] - a[fa[i]];
printf("%lld\n", ans);
}
十四、廉讯公司
题意:
转换一个128位的二进制串,变成16进制,并且格式为x : x : x : x : x : x : x : x然后多个相邻的0变成::,且只有一个::,求变成的字典序最小且最短的字符串。然后根据输入的字符串求最早出现时间.
思路:
先将二进制转化为十进制,然后用%x输出,因为连续的0可变短,但有优先级,首先选0长的,然后如果长度相等,先选中间,中间里面先选中间偏后,然后是最后,最后是前面。然后时间用map记录一下,这里注意时间最好转成秒,方便以小到大输出.
#include <iostream>
#include <algorithm>
#include <cstring>
#include <map>
#include <string>
using namespace std;
int main()
{
int n, H, M, S, t;
int a[10], r[10];
char s[150],tmp[150];
map<string, int> mp;
string str;
scanf("%d", &n);
for (int cas = 1; cas <= n; cas++)
{
scanf("%d:%d:%d--->%s",&H,&M,&S, s + 1);
t = H * 3600 + M * 60 + S;
for (int i = 1; i <= 8; i++)
{
a[i] = 0;
for (int j = 1; j <= 16; j++)
a[i] = a[i] * 2 + s[(i - 1) * 16 + j] - '0';
}
memset(r, 0, sizeof(r));
int id = 0;
for (int i = 8; i >= 1; i--)
{
if (a[i] == 0) r[i] = r[i + 1] + 1;
if (r[id] < r[i]) id = i;
if (r[id] == r[i] && r[id] + id - 1 == 8 && i != 1) id = i;
}
str.clear();
for (int i = 1; i <= 8; i++)
{
if (i == id && r[i] >= 2)
{
if (id == 1) str.append(":");
str.append(":");
i = i + r[i] - 1;
}
else
{
sprintf(tmp,"%x", a[i]);
str.append(tmp);
if (i != 8) str.append(":");
}
}
if(!mp[str])mp[str] = t;
cout << str << endl;
}
scanf("%s", s);
str = s;
t = mp[str];
cout << t/3600 << ":" << (t%3600)/60 << ":" << t%60 << endl;
}
十五、LFU
做法: 利用稀疏链表(双向)来解决存储结构问题
如果不用哈希表存<key, (行指针, 列指针)>, 就遍历所有链表找<key,value>
使用哈希表存<key, (行指针, 列指针)>, 可以直接O(1)查找值,然后进行链表节点转移
考虑这几个点
加入一个新点(set一个新的key)
- 如果链表不存在,则直接创建
- 如果链表存在, 且第一个链表代表次数1, 则添加至末端
- 如果链表存在, 但第一个链表不代表次数1, 则添加新的链表代表次数1
- 对于哈希表修改:添加<key, (行指针, 列指针)>
覆盖一个旧点(set一个已存在的key)
- 假设此时当前key所在节点为次数x的链表, 将其转移至次数为x+1的链表
- 如果下一个链表表示x+1, 直接将其接至末尾
- 如果下一个链表不表示x+1, 或者无链表, 新建代表x+1的链表
- 对于哈希表修改:修改原来的行指针
获得一个点的值(get一个已存在的key)
- 不存在不加点
- 存在, 同上,此时在x链表, 将其转移至x+1的链表
- 对于哈希表修改:修改原来的行指针
set TXT ORZ
set LT CAI
set TXT TQL
get TXT
考虑此样例
输入:
3
set 1 1
set 2 2
set 3 3
set 2 22
get 1
get 2
get 3
输出:
1
22
3
考虑其中set 2 22
参考代码(C++警告)
关于unodered_map的重载 自己百度一下呗
#include<unordered_map>
#include<iostream>
#include<vector>
#include<list>
#include<iterator>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
using namespace std;
template<class key_t, class val_t, class Hasher = hash<key_t>>
class LFU_Cache {
private:
size_t capacity;
size_t left_size;
using pkv_t = pair<key_t, val_t>;
using list_pkv_t = list<pkv_t>;
list< pair<size_t, list_pkv_t> >cache_list;
using row_iter_t = typename decltype(cache_list)::iterator;
using col_iter_t = typename list_pkv_t::iterator;
unordered_map<key_t, pair<row_iter_t, col_iter_t>, Hasher>mp;
public:
LFU_Cache(size_t capacity) :capacity(capacity) {
left_size = capacity;
}
/*
* get
* @return: {false, ...} represent not found
* {true, ...} represent found
*/
pair<bool, val_t> get(const key_t &key) {
auto p = mp.find(key);
if (p == mp.end()) {
return {
false, key_t() };
}
else {
p->second = to_next_times_list(p->second.first, p->second.second);
return {
true, (p->second.second)->second };
}
}
/*
* set
* @param key
* @param val
*
*/
void set(const key_t &key, const val_t &val) {
auto p = mp.find(key);
if (p == mp.end()) {
//not found
if (!left_size) remove();
auto row_iter = cache_list.begin();
if (row_iter == cache_list.end() || row_iter->first != 1) {
row_iter = cache_list.insert(row_iter, {
1, {
} });
}
list_pkv_t& col_list = row_iter->second;
auto col_iter = col_list.insert(col_list.end(), {
key, val });
mp[key] = {
row_iter, col_iter };
left_size--;
}
else {
//found
auto res = to_next_times_list(p->second.first, p->second.second);
p->second = res;
(p->second.second)->second = val;
}
}
private:
/*
* move current node to next times list,
* {x, y} -> {x + 1, last one}
*/
pair<row_iter_t, col_iter_t> to_next_times_list(row_iter_t row_iter, col_iter_t col_iter) {
list_pkv_t& col_list = row_iter->second;
//remove it to a new list
{
//remove
pkv_t _pkv = *col_iter; //save key, value
size_t rep_op = row_iter->first; // save the repeat time
col_list.erase(col_iter);
//insert to a new list
if (col_list.size() == 0)
row_iter = cache_list.erase(row_iter);
else
row_iter++;
//not a corresponding list, create and redirect to a new list
if (row_iter == cache_list.end() || row_iter->first != rep_op + 1) {
row_iter = cache_list.insert(row_iter, {
rep_op + 1, {
} });
}
list_pkv_t& col_list = row_iter->second;
//push back
col_iter = col_list.insert(col_list.end(), _pkv);
}
return {
row_iter, col_iter };
}
/*
* remove the first cache
*/
void remove() {
//no left
if (capacity == left_size) return;
auto row_iter = cache_list.begin();
list_pkv_t& col_list = row_iter->second;
auto col_iter = col_list.begin();
key_t key = col_iter->first;
mp.erase(key);
col_list.erase(col_iter);
if (col_list.size() == 0) cache_list.erase(row_iter);
left_size++;
}
};
struct string_hash {
static const size_t BASE = 131;
size_t operator() (const string& s) noexcept {
size_t res = 0;
for (auto it = s.begin(); it != s.end(); it++) {
res = res * BASE + *it;
}
return res;
}
};
int main() {
//freopen("C:\\Users\\Administrator\\Desktop\\data\\3.in", "r", stdin);
//freopen("C:\\Users\\Administrator\\Desktop\\data\\3.out", "w", stdout);
int size;
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> size;
LFU_Cache<string, string, hash<string>>lfu(size);
string op, s1, s2;
while (cin >> op) {
if (op == "get") {
cin >> s1;
auto p = lfu.get(s1);
if (p.first)
cout << p.second << endl;
else cout << "Not Found\n";
}
else if (op == "set") {
cin >> s1 >> s2;
lfu.set(s1, s2);
}
}
return 0;
}