看了题解的题目会标注
*
,题目按照 CF 默认顺序排序
C. Skier
题面说的是“路径”,而不是“点”。
可以设最初滑雪者的位置在 (1,1),用 (map) (vis[id(i,j)][id(k,h)])(坐标展开成一维)记录 ((i,j)) ((k,h)) 两点之间是否访问过。
#include <iostream>
#include <cstring>
#include <map>
using namespace std;
long long T, n, tot, x, y, ans;
string s;
map <long long, map <long long, long long> > vis;
map <long long, map <long long, long long> > id;
int main()
{
scanf("%lld", &T);
while(T--)
{
cin >> s; n = s.size();
x = 1, y = 1, ans = 0, tot = 0;
id[1][1] = ++tot;
for(int i = 0; i < n; i++)
{
int now = id[x][y];
if(s[i] == 'N') x--;
if(s[i] == 'S') x++;
if(s[i] == 'W') y--;
if(s[i] == 'E') y++;
if(!id[x][y]) id[x][y] = ++tot;
if(vis[now][id[x][y]] || vis[id[x][y]][now])
ans += 1;
else ans += 5;
vis[now][id[x][y]] = vis[id[x][y]][now] = 1;
}
x = y = 1;
for(int i = 0; i < n; i++)
{
int now = id[x][y];
if(s[i] == 'N') x--;
if(s[i] == 'S') x++;
if(s[i] == 'W') y--;
if(s[i] == 'E') y++;
vis[now][id[x][y]] = vis[id[x][y]][now] = 0;
}
id[1][1] = 0, x = y = 1;
for(int i = 0; i < n; i++)
{
if(s[i] == 'N') x--;
if(s[i] == 'S') x++;
if(s[i] == 'W') y--;
if(s[i] == 'E') y++;
id[x][y] = 0;
}
vis[1][1] = 0;
printf("%lld
", ans);
}
return 0;
}
C. Phoenix and Distribution
首先考虑第一个字母:从小到大选择,如果第一个字母不同,答案就是其中最大的一个;
剩下两种情况:所有的都摞在一起或平均分配,判断这两种情况谁的字典序小
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 233333;
int T, n, k, tot, cnt1 = 0, cnt2 = 0;
int a[N], c[N], d[N];
string s;
bool same(int x, int y)
{
for(int i = x + 1; i <= y; i++)
if(a[i] != a[i - 1]) return false;
return true;
}
bool cmp(int num1, int num2)
{
for(int i = 1; i <= min(num1, num2); i++)
{
if(c[i] != d[i] && c[i] < d[i]) return true;
if(c[i] != d[i] && c[i] > d[i]) return false;
}
if(num1 < num2) return true;
return false;
}
int main()
{
scanf("%d", &T);
while(T--)
{
scanf("%d%d", &n, &k);
cin >> s;
for(int i = 0; i < n; i++) a[i + 1] = s[i];
sort(a + 1, a + n + 1);
if(!same(1, k))
{
cout << (char)(a[k]) << "
";
continue;
}
tot = 1, cnt1 = cnt2 = 0;
while(tot <= n)
{
c[++cnt1] = a[min(n, tot + k - 1)];
if(!same(tot, min(n, tot + k - 1))) break;
tot += k;
}
d[++cnt2] = a[1];
for(int i = k + 1; i <= n; i++) d[++cnt2] = a[i];
if(cmp(cnt1, cnt2))
{
for(int i = 1; i <= cnt1; i++)
cout << (char)(c[i]);
printf("
");
}
else
{
for(int i = 1; i <= cnt2; i++)
cout << (char)(d[i]);
printf("
");
}
}
return 0;
}
B. Phoenix and Beauty
如果看做一个长度为 (k) 的窗口从左向右滑动,出去的必须等于进来的。所以,若数组中有超过 (k) 个元素,则输出 (-1)
否则,构造前 (k) 个里面包含所有元素,不断向右滑动,遇到相等的就领走初始数组中的元素,直至初始数组为空
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 23333;
int T, n, k, m, tot, a[N], use[N], b[N], c[N], d[N];
int main()
{
cin >> T;
memset(use, 0, sizeof(use));
while(T--)
{
scanf("%d%d", &n, &k);
int fl = 1, cnt = 0, tmp = 0;
for(int i = 1; i <= n; i++)
scanf("%d", &a[i]), c[i] = a[i];
sort(c + 1, c + n + 1);
for(int i = 1; i <= n; i++)
if(c[i] != c[i - 1] || i == 1) d[++tmp] = c[i];
for(int i = 1; i <= n; i++)
{
if(!use[a[i]]) cnt++;
use[a[i]] = 1;
}
if(cnt <= k)
{
tot = 1, m = k;
if(tmp >= k)
for(int i = 1; i <= k; i++) b[i] = d[i];
else
{
for(int i = 1; i <= k; i++) b[i] = 1;
for(int i = 1; i <= tmp; i++) b[i] = d[i];
}
while(tot <= n)
{
b[++m] = b[m - k];
if(b[m] == a[tot]) tot++;
}
printf("%d
", m);
for(int i = 1; i <= m; i++) printf("%d ", b[i]);
printf("
");
}
else printf("-1
");
for(int i = 1; i <= n; i++) use[a[i]] = 0;
}
return 0;
}
A. Hilbert's Hotel
房间数是无限的,无法枚举,考虑枚举 (a[1-n])。先不考虑取模,最初能 (+a[i]) 的就是 (i) 号房,顺序推下去,能 (+a[i]) 的还有类似 (i+n),(i+2n)...这些房间 (mod) (n) 都等于 (i),加 (a[i]) 之后再取膜也是相等的
若 (i) 号房和 (j) 号房转移到了同一个房间,说明 (i+xn+a[i]=j+a[j]) ((x) 是常数),此时 ((a[i]+i))%(n=(j+a[j]))%(n)。因此对于 (1≤i≤n),只需要标记((i+a[i])%n) 是否使用过
#include <iostream>
#include <map>
#include <cstdio>
using namespace std;
const int N = 2333333;
long long T, n, a[N], vis[N];
bool tmp;
int main()
{
scanf("%lld", &T);
while(T--)
{
scanf("%lld", &n);
tmp = true;
for(int i = 0; i < n; i++) scanf("%lld", &a[i]);
for(int j = 0; j < n; j++)
{
vis[(((j + a[j]) % n) + n) % n]++;
if(vis[(((j + a[j]) % n) + n) % n] > 1)
tmp = false;
}
for(int j = 0; j < n; j++)
vis[(((j + a[j]) % n) + n) % n] = 0;
if(tmp == true) printf("YES
");
else printf("NO
");
}
return 0;
}
C. Yet Another Counting Problem *
保证 (a<b),打表找规律得从 (lcm(a,b)*x)((x≥0) 且 (x) 是常数)开始 (b) 个数 %(a)%(b=)%(b)%(a)
注意特判 (x=0),判断边界
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
long long T, a, b, q, l, r, lcm;
long long get(long long x)
{
long long res = (x / lcm + 1) * b;
if(x % lcm < b) res -= b - 1, res += x % lcm;
if(x >= b) res - 1;
return max(0ll, res);
}
int main()
{
scanf("%lld", &T);
while(T--)
{
scanf("%lld%lld%lld", &a, &b, &q);
if(a > b) swap(a, b);
for(int i = a * b; i > 0; i--)
if(i % a == 0 && i % b == 0)
lcm = i;
for(int i = 1; i <= q; i++)
{
scanf("%lld%lld", &l, &r);
printf("%lld ", r - l + 1 - get(r) + get(l - 1));
}
printf("
");
}
return 0;
}
A. Nastya and Strange Generator
好像是写麻烦了...
注意到 (r) 数组不是变成 (0),就是有增无减。所以在改变位置 (i) 时,使用并查集维护,把 (r_i) 与 (r_{i+1}) 合并。然后再用线段树维护 (count) 的最值
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define mid ((l + r) / 2)
using namespace std;
const int N = 233333;
int T, n, x, max_, fl, a[N], cnt[N], fa[N], t[6666666];
int find(int x)
{
return x == fa[x] ? x : fa[x] = find(fa[x]);
}
void build(int x, int l, int r)
{
if(l == r)
{
t[x] = 1;
return ;
}
build(x * 2, l, mid);
build(x * 2 + 1, mid + 1, r);
t[x] = max(t[x * 2], t[x * 2 + 1]);
}
void update(int x, int l, int r, int std, int k)
{
if(std > r || std < l) return ;
if(l >= r)
{
t[x] += k;
return ;
}
update(x * 2, l, mid, std, k);
update(x * 2 + 1, mid + 1, r, std, k);
t[x] = max(t[x * 2], t[x * 2 + 1]);
}
int main()
{
scanf("%d", &T);
while(T--)
{
scanf("%d", &n);
build(1, 1, n);
fl = max_ = 1, fa[n + 1] = n + 1;
for(int i = 1; i <= n; i++)
{
scanf("%d", &x);
a[x] = i; fa[i] = i;
cnt[i] = 1;
}
for(int i = 1; i <= n; i++)
{
if(cnt[find(a[i])] != t[1])
{
fl = 0;
printf("No
");
break;
}
int x = find(a[i] + 1), y = find(a[i]);
if(x != n + 1) update(1, 1, n, x, cnt[y]);
update(1, 1, n, y, -cnt[y]);
cnt[x] += cnt[y];
cnt[y] = 0;
fa[y] = x;
}
if(fl == 1) printf("Yes
");
}
return 0;
}
A. Powered Addition
记 (maxx[i]) 表示 (i) 前面(不含)(a[j]) 的最大值,(a[i]) 最小变为 (maxx[i])。因为它们的差恰好能被一些 (2) 的次方数唯一地凑出来,将它改正的时间就是其中的 (k),使 (2^k) 是 (≤maxx[i]-a[i]) 的最大整数
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 233333;
long long T, n, tot, ans, a[N], base[N], now;
long long check(long long x)
{
for(int i = tot; i >= 0; i--)
if(x >= base[i]) return i + 1;
}
int main()
{
scanf("%lld", &T);
tot = -1;
for(long long i = 1; i <= 2000000000; i *= 2)
base[++tot] = i;
while(T--)
{
scanf("%lld", &n);
for(int i = 1; i <= n; i++) scanf("%lld", &a[i]);
ans = 0, now = a[1];
for(int i = 1; i <= n; i++)
{
if(now > a[i])
{
ans = max(ans, check(now - a[i]));
a[i] = now;
}
now = max(now, a[i]);
}
printf("%lld
", ans);
}
return 0;
}
A. Linova and Kingdom
先不考虑工业城市的放法,考虑旅游城市的放法。共需要放 (n-k) 个旅游城市,如果它的子树中所有节点都是工业城市,那么在 (i) 号放一个旅游城市带来的贡献是 (siz[i])(即以 (i) 为根的子树的大小)。如果它有 (x) 个祖宗(包括 (i) 自己),那么这些祖宗的贡献值都要被 (-1)。
因为在某个结点放旅游城市时,它的所有祖宗结点肯定已经全部变成了旅游城市,只要按照 (siz[i]-1-dep[i]) 排序即可(根节点的深度为 (0))
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 2333333;
struct edge { int nxt, to; } e[N];
struct srt { int id; long long val; } st[N];
int n, k, a, b, cnt = 0, head[N], siz[N], dep[N];
long long ans = 0;
bool cmp(srt x, srt y) { return x.val > y.val; }
void add(int x, int y)
{
e[++cnt] = (edge) { head[x], y };
head[x] = cnt;
}
void dfs(int x, int fa)
{
siz[x] = 1;
dep[x] = dep[fa] + 1;
for(int i = head[x]; i; i = e[i].nxt)
{
int v = e[i].to;
if(v == fa) continue;
dfs(v, x);
siz[x] += siz[v];
}
}
int main()
{
scanf("%d%d", &n, &k);
dep[0] = -1;
memset(head, 0, sizeof(head));
for(int i = 1; i < n; i++)
{
scanf("%d%d", &a, &b);
add(a, b), add(b, a);
}
dfs(1, 0);
for(int i = 1; i <= n; i++)
{
st[i].id = i;
st[i].val = siz[i] - dep[i] - 1;
}
sort(st + 1, st + n + 1, cmp);
for(int i = 1; i <= n - k; i++) ans += st[i].val;
printf("%lld", ans);
return 0;
}
C. Circle of Monsters *
嗯看的官方题解 https://codeforces.com/blog/entry/75877
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const long long INF = 1e18;
long long T, n, num, ans, a[2333333], b[2333333];
int main()
{
scanf("%lld", &T);
while(T--)
{
scanf("%lld", &n);
num = 0, ans = INF;
for(int i = 1; i <= n; i++)
scanf("%lld%lld", &a[i], &b[i]);
for(int i = 1; i <= n; i++)
{
int pre = i - 1;
if(i == 1) pre = n;
num += max(0ll, a[i] - b[pre]);
}
for(int i = 1; i <= n; i++)
{
int pre = i - 1;
if(i == 1) pre = n;
ans = min(ans, num - max(0ll, a[i] - b[pre]) + a[i]);
}
printf("%lld
", ans);
}
return 0;
}
C. K-Complete Word *
要想满足题面要求,字符串不仅本身是回文串,每 (k) 位也应该构成回文串。即求所有的 (i,k*j+i,k*j+k-i+1) 相等。对于每个 (i),在所有这些位置里面选出出现最多的字母,都更改为这个字母。
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 233333;
int T, n, k, ans, num, max_, a[N], cnt[N];
string s;
int main()
{
scanf("%d", &T);
memset(cnt, 0, sizeof(cnt));
while(T--)
{
scanf("%d%d", &n, &k);
cin >> s;
ans = 0;
for(int i = 0; i < n; i++) a[i + 1] = s[i];
for(int i = 1; i <= k; i++)
{
num = max_ = 0;
for(int j = 0; j * k + i <= n; j++)
{
cnt[a[j * k + i]]++;
cnt[a[j * k + k - i + 1]]++;
num += 2;
max_ = max(max_, cnt[a[j * k + i]]);
max_ = max(max_, cnt[a[j * k + k - i + 1]]);
}
for(int j = 0; j * k + i <= n; j++)
{
cnt[a[j * k + i]] = 0;
cnt[a[j * k + k - i + 1]] = 0;
}
ans += num - max_;
}
printf("%d
", ans / 2);
}
return 0;
}
B. Composite Coloring *
看题解三连...
每个合数 (x) 都能整除一个最小的质数,这个质数 (≤sqrt(x))。而 (sqrt(1000)) 以下正好有 (11) 个质数,所以直接把每个 (i) 分到它能整出的最小的质数组。
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 23333;
int T, n, color, num[N], a[N], col[N];
int p[] = {0, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31};
int main()
{
scanf("%d", &T);
while(T--)
{
scanf("%d", &n);
color = 0;
for(int i = 1; i <= 11; i++) num[i] = 0;
for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
for(int i = 1; i <= n; i++)
for(int j = 1; j <= 11; j++)
if(a[i] % p[j] == 0)
{
if(!num[j]) num[j] = ++color;
col[i] = num[j];
break;
}
printf("%d
", color);
for(int i = 1; i <= n; i++) printf("%d ", col[i]);
printf("
");
}
return 0;
}
B. Dreamoon Likes Permutations
如果一个区间有恰好 (m) 个元素,且这些元素的最大值是 (m),说明当前区间符合条件
从前往后,从后往前分别记录 (1-i) 是否符合条件、(i-n) 是否符合条件,枚举断点判断即可
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 2333333;
int T, n, ans, max_, num, a[N], t1[N], t2[N], p1[N], p2[N];
int main()
{
scanf("%d", &T);
while(T--)
{
scanf("%d", &n);
ans = 0;
for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
for(int i = 1; i <= n; i++)
p1[i] = p2[i] = false, t1[a[i]] = t2[a[i]] = 0;
max_ = num = 0;
for(int i = 1; i <= n; i++)
{
if(!t1[a[i]]) max_ = max(max_, a[i]), num++;
else break;
if(max_ == num) p1[i] = true;
t1[a[i]]++;
}
max_ = num = 0;
for(int i = n; i > 0; i--)
{
if(!t2[a[i]]) max_ = max(max_, a[i]), num++;
else break;
if(max_ == num) p2[i] = true;
t2[a[i]]++;
}
for(int i = 1; i < n; i++)
if(p1[i] && p2[i + 1]) ans++;
printf("%d
", ans);
for(int i = 1; i < n; i++)
if(p1[i] && p2[i + 1])
printf("%d %d
", i, n - i);
}
return 0;
}
C. Game with Chips
有一种奇妙的构造方式,就是先向右移,把所有棋子都移到最后一列;然后都移到第一行。这时 (k) 个棋子变成了“一个棋子”。让这些棋子从右上角开始遍历整个棋盘。可以证明这种构造方式的路径总长度不会超过 (2mn)(因此不存在 (-1) 的情况)
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
int T, n, m, k, x, y;
int main()
{
scanf("%d%d%d", &n, &m, &k);
for(int i = 1; i <= k * 2; i++) scanf("%d%d", &x, &y);
printf("%d
", m + n + m * n - 1);
for(int i = 1; i <= m; i++) printf("R");
for(int i = 1; i <= n; i++) printf("U");
for(int i = 1; i <= n; i++)
{
if(i % 2 == 0)
for(int j = 1; j < m; j++) printf("R");
else for(int j = 1; j < m; j++) printf("L");
if(i != n) printf("D");
}
return 0;
}
D1. Prefix-Suffix Palindrome (Easy version) *
先判断最长有多长的前后缀相等,然后判断中间最长的回文串(左端点与最长前缀相连或右端点与最长的后缀相连)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 233333;
int T, n, l, r, l1, l2, tag, a1, a2, a[N];
string s, ans;
bool check(int x, int y)
{
while(a[x] == a[y] && x <= n && y > 0) x++, y--;
return x >= y;
}
int main()
{
scanf("%d", &T);
while(T--)
{
cin >> s; n = s.size();
for(int i = 1; i <= n; i++) a[i] = s[i - 1];
if(n == 1 || check(1, n)) { cout << s << "
"; continue; }
l = 1, r = n;
while(a[l] == a[r]) l++, r--;
ans = "", a1 = l, a2 = r;
for(int i = l; i <= r; i++) if(check(l, i)) a1 = i;
for(int i = r; i >= l; i--) if(check(i, r)) a2 = i;
if(a1 - l >= r - a2)
{
for(int i = 1; i <= a1; i++) ans += a[i];
for(int i = r + 1; i <= n; i++) ans += a[i];
}
else
{
for(int i = 1; i < l; i++) ans += a[i];
for(int i = a2; i <= n; i++) ans += a[i];
}
cout << ans << endl;
}
return 0;
}
C. Ehab and Path-etic MEXs
为了不让大权值成为路径上的“最小的未出现”,需要尽量让它出现在路径上。经过某条边的路径数 (val[i]=siz[i]*(n-siz[i]))((siz[i]) 表示 (i) 边向下的子树大小)。将边按照 (val) 从大到小排序,(val) 大的边放大的权值
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define LL long long
using namespace std;
const int N = 233333;
struct edge { LL nxt, to, val, id; } e[N];
LL n, a, b, cnt = 0, head[N], ans[N], siz[N];
void add(LL x, LL y)
{
e[++cnt] = (edge) { head[x], y, 0, cnt };
head[x] = cnt;
}
void dfs(LL x, LL fa)
{
siz[x] = 1;
for(int i = head[x]; i; i = e[i].nxt)
{
LL v = e[i].to;
if(v == fa) continue;
dfs(v, x);
siz[x] += siz[v];
e[i].val = siz[v] * (n - siz[v]);
}
}
bool cmp(edge x, edge y) { return x.val > y.val; }
int main()
{
scanf("%lld", &n);
memset(ans, -1, sizeof(ans));
for(int i = 1; i < n; i++)
{
scanf("%lld%lld", &a, &b);
add(a, b); add(b, a);
}
dfs(1, 0);
sort(e + 1, e + cnt + 1, cmp);
for(int i = 1; i < n; i++)
ans[e[i].id] = n - 2 - i + 1;
for(int i = 1; i <= cnt; i++)
if(ans[i] != -1) printf("%lld
", ans[i]);
return 0;
}
D. Pair of Topics
为了使 (i) 和 (j) 都在一边,把这个式子移项:(a[i]-b[i]=-a[j]+b[j])。先按照 (-a[i]+b[i]) 排序,对于每个 (i) 进行二分查找,统计答案。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define LL long long
using namespace std;
const int N = 233333;
struct edge { LL a, b; } e[N];
LL n, ans = 0;
bool cmp(edge x, edge y) { return x.b - x.a < y.b - y.a; }
LL check(LL x)
{
LL l = 0, r = n;
while(l + 1 < r)
{
LL mid = (l + r) >> 1;
if(e[mid].b - e[mid].a < x) l = mid;
else r = mid;
}
if(e[r].b - e[r].a < x) return r;
return l;
}
int main()
{
scanf("%lld", &n);
for(int i = 1; i <= n; i++) scanf("%lld", &e[i].a);
for(int i = 1; i <= n; i++) scanf("%lld", &e[i].b);
sort(e + 1, e + n + 1, cmp);
for(int i = 1; i <= n; i++)
ans += check(e[i].a - e[i].b);
for(int i = 1; i <= n; i++)
if(e[i].a * 2 > e[i].b * 2) ans--;
printf("%lld", ans / 2);
return 0;
}
B. Count Subrectangles
稍加观察可以发现,(a) 中任何一段连续的 (1) 和 (b) 中任何一段连续的 (1) 都可以组成一个矩阵。那么对于每一段连续的 (1),我们都只关心它的长度,而不关心它的位置。可以利用差分统计一下每个长度都出现了几次,选择能被 (k) 整除的 (i) 统计答案
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 500000;
long long n, m, k, len, ans = 0, a, b, p1[N], p2[N];
int main()
{
scanf("%lld%lld%lld", &n, &m, &k);
len = 0;
memset(p1, 0, sizeof(p1));
memset(p2, 0, sizeof(p2));
for(int i = 1; i <= n; i++)
{
scanf("%lld", &a);
if(a == 0) len = 0;
else len++;
p1[1]++, p1[len + 1]--;
}
len = 0;
for(int i = 1; i <= m; i++)
{
scanf("%lld", &b);
if(b == 0) len = 0;
else len++;
p2[1]++; p2[len + 1]--;
}
for(int i = 1; i <= n; i++) p1[i] += p1[i - 1];
for(int i = 1; i <= m; i++) p2[i] += p2[i - 1];
for(int i = 1; i <= n; i++)
if(k % i == 0 && k / i <= m)
ans += p1[i] * p2[k / i];
printf("%lld", ans);
return 0;
}
C. Remove Adjacent *
每次选择一个最大能删除的字母删除,因为它不会影响其他字母的删除。证明如下:
(1.) 序列中不存在字母比它大,删它肯定不影响
(2.) 序列中存在字母比它大,中间没隔着任何字母。这种情况实际上不存在,因为这种情况下这个字母不是最大可删除的字母
(3.) 序列中存在字母比他大,中间隔着一些字母,可以证明中间的字母不可能删干净。
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 2333;
int n, ans = 0, a[N], del[N];
string s;
int get(int x, int k)
{
int tot = 0;
if(k < 0)
{
for(int i = x - 1; i > 0; i--)
if(del[i] == false) return a[i];
return 0x3f3f3f3f;
}
for(int i = x + 1; i <= n; i++)
if(del[i] == false) return a[i];
return 0x3f3f3f3f;
}
int main()
{
scanf("%d", &n);
cin >> s;
memset(a, 0x3f, sizeof(a));
memset(del, false, sizeof(del));
for(int i = 1; i <= n; i++) a[i] = s[i - 1];
while(true)
{
int max_ = 0, id = -1;
for(int i = 1; i <= n; i++)
if(!del[i] && (get(i, -1) == a[i] - 1 || get(i, 1) == a[i] - 1))
if(a[i] > max_) max_ = a[i], id = i;
if(id == -1) break;
del[id] = true;
}
for(int i = 1; i <= n; i++) if(del[i]) ans++;
printf("%d", ans);
return 0;
}
A. Journey Planning
将这个式子移项得:(i-b[i]=j-b[j])。设 (val[i]=i-b[i]),按照 (val) 为排序。对于每一个 (val),统计所有 (val) 值相等的数的和,在里面取最大值作为答案
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 2333333;
struct node { long long id, b, val; } a[N];
long long n, ans = 0, sum = 0;
bool cmp(node x, node y)
{
return x.b == y.b ? x.id < y.id : x.val < y.val;
}
int main()
{
scanf("%lld", &n);
for(int i = 1; i <= n; i++)
{
scanf("%lld", &a[i].b);
a[i].id = i;
a[i].val = a[i].id - a[i].b;
}
sort(a + 1, a + n + 1, cmp);
for(int i = 1; i <= n; i++)
{
if(a[i].val != a[i - 1].val || i == 1)
{
ans = max(ans, sum);
sum = a[i].b;
}
else sum += a[i].b;
}
printf("%lld", max(ans, sum));
return 0;
}
B. String Modification *
(*1400) 还要看题解的垃圾的我...
所以不写辣,甩上题解(逃 https://codeforces.com/blog/entry/74493
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 233333;
int T, n, id, a[N];
string s;
int get(int x, int pos)
{
if(pos <= n - x + 1) return a[pos + x - 1];
if((n % 2) == (x % 2)) return a[x - (pos - (n - x + 1))];
return a[pos - n + x - 1];
}
int main()
{
scanf("%d", &T);
while(T--)
{
scanf("%d", &n);
cin >> s;
id = 1;
for(int i = 1; i <= n; i++) a[i] = s[i - 1];
for(int i = 2; i <= n; i++)
for(int j = 1; j <= n; j++)
{
if(get(i, j) < get(id, j))
{
id = i;
break;
}
if(get(i, j) > get(id, j)) break;
}
for(int j = 1; j <= n; j++) cout << (char)(get(id, j));
printf("
%d
", id);
}
}
其实后面还做过一些,现在开学了,就先鸽着吧...