比赛链接: https://www.luogu.com.cn/contest/121860#description
A - KUTEVI
解题思路
一道初见比较难入手的题, 觉得一时间找不到合适的算法, 但是仔细观察之后会发现他的题目描述和完全背包有些相似, 而问法和之前做过的一道布尔类型的dp题很像; 所以我们状态表示dp[i]表示能否凑到角度i, i可以由i+a减a或者i-a加a获得; 又因为本题角度不能小于0且不能大于360, 所以要对360取模, 而j也要遍历到大于720才行; 具体代码如下;
神秘代码
#include<bits/stdc++.h>
//#define int long long
using namespace std;
typedef pair<int, int> PII;
const int N = 1e6 + 10, mod = 998244353;
int n, m, x, y;
int a[100], b[100];
int d[4000];
signed main(){
cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = 1; i <= m; i++) cin >> b[i];
d[0] = 1;
for (int i = 1; i <= n; i++) {
for (int j = a[i]; j <= 1000;j++) {
d[j % 360] |= d[(j - a[i]) % 360];
d[j % 360] |= d[(j + a[i]) % 360];
}
}
for (int i = 1; i <= m; i++) {
if (d[b[i]]) cout << "YES" << endl;
else cout << "NO" << endl;
}
return 0;
}
B - 高手之在一起
解题思路
签到题, 唯一需要注意的是如果我们需要用getline输入, 那我们需要先用getchar()吸收掉前面n和m后面的换行符; 否则getline会直接从缓冲区读入换行符;
神秘代码
#include<bits/stdc++.h>
//#define int long long
using namespace std;
typedef pair<int, int> PII;
const int N = 1e6 + 10, mod = 1e9 + 7;
map<string, int> mp;
int n, m, idx;
signed main() {
cin >> n >> m;
getchar();
for (int i = 1; i <= n; i++) {
string s;
getline(cin, s);
mp[s]++;
}
for (int i = 1; i <= m; i++) {
string s;
getline(cin, s);
if (mp[s]) idx++;
}
cout << idx;
return 0;
}
C - 密文搜索
解题思路
因为密文只要求连续不要求顺序, 那我们可以把所有密文从大到小排序后用map存起来; 然后对资料进行遍历, 滑动地向后取8个字符, 然后把这8个字符也从大到小排序, 看是否是密文即可;
神秘代码
#include<bits/stdc++.h>
//#define int long long
using namespace std;
typedef pair<int, int> PII;
const int N = 1e6 + 10, mod = 1e9 + 7;
map<string, int> mp;
int n, m, res;
string s;
signed main() {
cin >> s;
cin >> n;
for (int i = 1; i <= n; i++) {
string str;
cin >> str;
sort(str.begin(), str.end());
mp[str]++;
}
for (int i = 0; i < s.size()-7; i++) {
string str = s.substr(i, 8);
sort(str.begin(), str.end());
res += mp[str];
}
cout << res;
return 0;
}
F - 合并序列
解题思路
一道比较简单的trie树的题目; 把所有字符串用trie树存起来后先遍历前缀字符串, 然后再用dfs找到所有后续的字符串即可;
神秘代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef pair<int, int> PII;
const int N = 1e6 + 10, mod = 1e9 + 7;
int s[N][26];
int h[N];
multiset<string> res;
int n, m, idx;
void ins(string str) {
int p = 0;
for (int i = 0; i < str.size(); i++) {
int u = str[i] - 'a';
if (s[p][u] == 0) {
s[p][u] = ++idx;
}
p = s[p][u];
}
h[p]++;
}
void dfs(int p, string str) {
if (h[p]) {
for (int i = 1; i <= h[p]; i++) res.insert(str);
}
for (int i = 0; i < 26; i++) {
char c = 'a' + i;
if (s[p][i]) {
dfs(s[p][i], str + c);
}
}
}
int find(string str) {
int p = 0;
for (int i = 0; i < str.size(); i++) {
int u = str[i] - 'a';
if (s[p][u] == 0) return 0;
p = s[p][u];
}
dfs(p,str);
}
signed main() {
cin >> n;
string str;
for (int i = 1; i <= n; i++) {
cin >> str;
ins(str);
}
cin >> str;
find(str);
for (string t : res) {
cout << t << endl;
}
return 0;
}
G - 最长的回文 Calf Flac
解题思路
这个题细节思路不难但细节偏多; 首先是要输入带空格和回车的字符串, 因为本文要求保留其中的换行符, 所以在拼接时需要手动加上; 再就是寻找回文串的方法可以用a[i]=a[i-1] || a[i]=a[i-2]; 这表示以第i个字符为回文串的中间右一的位置, 如果回文串长度为偶数, a[i]=a[i-1]; 如果为奇数则a[i]=a[i-2]; 而本题因为其他字符的干扰所以我们需要去筛选找到合适的字符;
在做本题是注意到了一个细节, 对于string s, 如果s="\n"会输出换行, 而s+='\', s+='n'就会输出"\n"而不是换行;
神秘代码
#include<bits/stdc++.h>
//#define int long long
using namespace std;
typedef pair<int, int> PII;
const int N = 1e6 + 10, mod = 1e9 + 7;
map<string, int> mp;
int n, m, res = 1;
string s, s1;
bool check1(char c) {//检查是否在字母范围内
if (c >= 'a' && c <= 'z') return true;
if (c >= 'A' && c <= 'Z') return true;
return false;
}
int check2(int x) {//往左找合适的字符
if (x < 0) return -1;
while (!check1(s[x]) && x - 1 >= 0) x--;
if (check1(s[x])) return x;
else return -1;
}
int check3(int x) {//往右找合适的字符
if (x >= s.size()) return -1;
while (!check1(s[x]) && x + 1 < s.size()) x++;
if (check1(s[x])) return x;
else return -1;
}
bool check4(char a, char b) {//字母大小写被视为相同的字符
if (a > b) swap(a, b);
if (a == b) return true;
else if (a + 32 == b) return true;
else return false;
}
int find(int l, int r, int idx) {
while (1) {
int a = check2(l - 1);
int b = check3(r + 1);
if (a == -1 || b == -1) break;
else {
if (check4(s[a], s[b])) {
l = a, r = b;
idx += 2;
}
else break;
}
}
if (idx > res) {
int len = r - l + 1;
s1 = s.substr(l, len);
res = idx;
}
return idx;
}
signed main() {
string t;
while (getline(cin, t)) s += t + '\n';
s1 = s[0];
for (int i = 0; i < s.size(); i++) {
if (!check1(s[i])) continue;
int a = check2(i - 1);
if (a != -1) {
if (check4(s[i], s[a])) find(a, i, 2);
int b = check2(a - 1);
if (b != -1) if (check4(s[i], s[b])) find(b, i, 3);
}
}
cout << res << endl << s1 << endl;
return 0;
}
H - Prawnicy
解题思路
一道比较典型的区间贪心; 把所有区间按照左端点从小到大排序, 这样遍历时当前区间的左端点就是区间交集的左端点; 然后用堆来维护区间交集的右端点即可; 因为排序后编号会边, 所以我们用结构体来存初识的编号;
神秘代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef pair<int, int> PII;
const int N = 3e6 + 10, mod = 1e9 + 7;;
int n, m, res, idx;
int heap[N];
int p[N], h[N], re[N];
struct str {
int l, r, id;
}f[N];
bool cmp(str a, str b) {
return a.l < b.l;
}
void down(int a) {
int u = a;
if (2 * a <= idx && heap[u] > heap[2 * a]) u = 2 * a;
if (2 * a + 1 <= idx && heap[u] > heap[2 * a + 1]) u = 2 * a + 1;
if (u != a) {
swap(heap[a], heap[u]);
down(u);
}
}
void up(int a) {
while (a / 2 >= 1 && heap[a] < heap[a / 2]) {
swap(heap[a], heap[a / 2]);
a = a / 2;
}
}
signed main() {
scanf("%d%d",&n,&m);
for (int i = 1; i <= n; i++) {
int a, b;
scanf("%d%d",&a,&b);
f[i] = { a,b,i };
}
sort(f + 1, f + 1 + n,cmp);
int l, r;
for (int i = 1; i <= n; i++) {
int a = f[i].l, b = f[i].r;
idx++;
heap[idx] = b;
up(idx);
if (idx == m) {
int d = heap[1] - a;
if (d > res) {
res = d;
l = a, r = heap[1];
}
}
else if (idx > m) {
swap(heap[1], heap[idx]);
idx--;
down(1);
int d = heap[1] - a;
if (d > res) {
res = d;
l = a, r = heap[1];
}
}
}
printf("%lld\n", res);
int num = 0;
for (int i = 1; i <= n; i++) {
int a = f[i].l, b = f[i].r;
if (a <= l && b >= r&&num<m) {
re[num++] = f[i].id;
}
}
sort(re, re + num);
for (int i = 0; i < num;i++) {
printf("%lld ", re[i]);
}
return 0;
}
I - 隐藏口令 Hidden Password
解题思路
这是一道关于字符串最小表示法的模板题, 直接暴力会爆内存; 以"anabana"为例简单说一下最小表示法, 我们让指针i和j分别指向字符串的前2个字符'a'和'n'; 因为'a'<'n', 所以我们需要把j往右移去找其他可能的最小值, 于是j到达了第3个字符'a', 这时我们引进一个偏移量k, 因为i和j所指的字符相同, 所以我们让k=1, 比较i+k和j+k所指字符的大小; 因为此时'b'<'n'所以要将i移位, 因为i~i+k都已经判断完了, 所以我们把i移到i+k+1; 这就是最小表示法的简单理解, 里面还有一些小细节, 如果移位后i=j, 则我们让j++; 为了防止i+k越界, 我们可以用字符串长度取余并且把k限制为小于字符串长度;
神秘代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef pair<int, int> PII;
const int N = 5e6 + 10, mod = 1e9 + 7;
int n, m;
char s[N];
int check(int len) {
int i, j, k;
i = 0; j = 1; k = 0;
while (i < len && j < len && k < n){
if (s[(i + k) % len] == s[(j + k) % len] && k < len) k++;
else {
if (s[(i + k) % len] > s[(j + k) % len])i = i + k + 1;
else j = j + k + 1;
if (i == j)j++;
k = 0;
}
}
return min(i, j);
}
signed main() {
cin >> n;
for (int i = 0; i < n; i++)cin >> s[i];
int res = check(n);
cout << res << endl;
return 0;
}
标签:heap,idx,int,res,30,long,个人赛,str
From: https://www.cnblogs.com/mostimali/p/17594431.html