直到南昌的题都补完了才发现徐州的题我还没补(甚至blog都没写(((((((日哦
比完出来拿出手机发现beginend他们10多分钟前就AK了(而我连题都没补x
A
生搬硬套傻逼题。
显然就EXCRT求一下n,然后手推一下,发现,诶2 3 5都可以诶,4 6 7都不可以诶,再看看样例,8也可以诶。
这tmd不就是斐波那契数列吗
然后大力斐波那契就完事了。
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <cstdint>
#include <utility>
using i128 = __int128;
using i64 = long long;
void Exgcd(i128 a, i128 b, i128& d, i128& x, i128& y) {
if (b == 0) {
d = a;
x = 1, y = 0;
return;
}
Exgcd(b, a % b, d, y, x);
y -= x * (a / b);
}
std::pair<i64, i64> CRT(std::pair<i64, i64> a[], int n) {
for (int i = 0; i < n; ++i)
if (a[i].first == -1)
return { -1, -1 };
i128 r1 = a[0].first, m1 = a[0].second;
bool flag = 0;
for (int i = 1; i < n; ++i) {
i128 m2 = a[i].second, r2 = a[i].first, d, x, y;
if (flag)
continue;
Exgcd(m1, m2, d, x, y);
i128 c = r2 - r1;
if (c % d) {
flag = 1;
continue;
}
i128 t = m2 / d;
x = (c / d * x % t + t) % t;
r1 = m1 * x + r1;
m1 = m1 * m2 / d;
r1 %= m1;
}
if (flag)
return { -1, -1 };
return { r1, m1 };
}
std::pair<i64, i64> a[15];
int main() {
std::ios::sync_with_stdio(0);
i64 n;
std::cin >> n;
for (int i = 0; i < n; ++i)
std::cin >> a[i].second >> a[i].first;
auto ans = CRT(a, n);
if (ans.first == -1) {
std::cout << "Tankernb!" << '\n';
return 0;
}
n = ans.first;
i64 t1 = 1, t2 = 2;
for (; t2 <= n; t1 += t2, std::swap(t1, t2)) {
if (t2 == n) {
std::cout << "Lbnb!" << '\n';
return 0;
}
}
std::cout << "Zgxnb!" << '\n';
}
B
傻叉卡常题,显然就把每个数和这个数+1放一起处理一下就好了。
可是计蒜客竟然
200
w
200w
200w次set操作就飞了????(赛后看群发现加超级快读就行。
然后当时就改成了离散并查集了。
#include <iostream>
#include <algorithm>
using namespace std;
inline int read() {
int d = 0;
char s = getchar();
while (s < '0' || s > '9') {
s = getchar();
}
while (s >= '0' && s <= '9') {
d = d * 10 + s - '0';
s = getchar();
}
return d;
}
int const N = 1000005;
struct node {
int x, y;
int pos;
}q[N];
int f[N << 1];
int det[N << 1];
int cnt;
int n, m;
int unfind(int x) {
int root = x, t;
while (f[root] != root)
root = f[root];
while (x != f[x]) {
t = f[x];
f[x] = root;
x = f[x];
}
return root;
}
int main() {
int n = read(), m = read();
for (int i = 1; i <= m; ++i) {
q[i].x = read(), q[i].y = read();
det[++cnt] = q[i].y;
if (q[i].y < n)
det[++cnt] = q[i].y + 1;
}
det[++cnt] = n + 1;
sort(det + 1, det + cnt + 1);
cnt = unique(det + 1, det + cnt + 1) - (det + 1);
for (int i = 1; i <= m; ++i)
q[i].pos = lower_bound(det + 1, det + cnt + 1, q[i].y) - det;
for (int i = 1; i <= cnt; ++i)
f[i] = i;
for (int i = 1; i <= m; ++i) {
int x = q[i].x, y = q[i].y;
if (x == 1) {
f[q[i].pos] = q[i].pos + 1;
} else {
int dx = unfind(q[i].pos);
if (dx == cnt)
puts("-1");
else
printf("%d\n", det[dx]);
}
}
}
C
这是真的想给出题人塞一百本英语读本。
a half指不均等的一半海星?????不过赛后查了下a halves好像是可以表示不均等的两半,但是不管如何a half都是均等的两半(虽然还有其他的语法锅但是就不吐槽了。所以真正题目的意思就是能随便切成两块,然后每块都是2的倍数就行。
#include <iostream>
#include <cstdio>
using namespace std;
int main() {
int n;
cin >> n;
if (n != 2 && (n & 1) == 0)
puts("YES");
else
puts("NO");
}
D
kmp直接判就完事了
#include <iostream>
#include <string.h>
using namespace std;
const int maxn = 100010;
int nxts[maxn], nxtt[maxn];
void getnxt(char* s,int* nxt, int n) {
int i = 0,j = -1;
nxt[0] = -1;
while(i < n) {
while(j != -1 && s[i] != s[j]) j = nxt[j];
++i,++j;
nxt[i] = j;
}
}
bool kmp(char* s,char* t, int* nxt, int n, int m) {
int i = 0,j = 0;
while(i < n) {
while(j != -1 && s[i] != t[j]) j = nxt[j];
++i;++j;
if(j == m) return true;
}
return false;
}
char s[maxn];
char t[maxn];
int main() {
scanf("%s",s);
int n = strlen(s);
getnxt(s, nxts, n);
int T;
scanf("%d",&T);
while(T--) {
scanf("%s",t);
int m = strlen(t);
if(m == n) {
if(kmp(t, s, nxts, m, n)) puts("jntm!");
else puts("friend!");
}
else if(m > n) {
if(kmp(t, s, nxts, m, n)) puts("my teacher!");
else puts("senior!");
}
else {
getnxt(t, nxtt, m);
if(kmp(s, t, nxtt, n, m)) puts("my child!");
else puts("oh, child!");
}
}
return 0;
}
E
倒过来维护一个序列,然后二分就完事了。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
int main () {
std::ios::sync_with_stdio(false);
int n, m;
std::cin >> n >> m;
auto w = std::vector<int>(n);
for (auto &c: w)
std::cin >> c;
auto ss = std::vector<std::pair<int, int>>(n);
ss.reserve(n);
auto ans = std::vector<int>(n, 0);
for (int i = n - 1; i >= 0; --i) {
auto it = std::lower_bound(std::begin(ss), std::end(ss), std::make_pair(w[i] + m, 0));
ans[i] = (it == std::end(ss) ? -1 : it->second - i - 1);
if (ss.empty() || ss.back().first < w[i])
ss.push_back({ w[i], i });
}
bool fst = true;
for (auto c: ans) {
if (fst)
fst = false;
else
std::cout << ' ';
std::cout << c;
}
std::cout << '\n';
return 0;
}
F
G
直接PAM上计数就好
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
int const N = 300015;
int nxt[N][26];
int fail[N];
int len[N];
int s[N], R;
int cont[N];
int cnt[N];
int col[N];
int last;
int p;
int newNode(int _len) {
for (int i = 0; i < 26; ++i)
nxt[p][i] = 0;
cont[p] = 0;
len[p] = _len;
return p++;
}
void init(int n) {
p = 0;
newNode(0);
newNode(-1);
fill(begin(s), end(s), -1);
fill(begin(cnt), end(cnt), 0);
last = 1;
fail[0] = 1;
}
int getFail(int v) {
int bord = R;
int cons = -1;
while (s[bord + cons * (len[v] + 1)] != s[bord])
v = fail[v];
return v;
}
void add(int c) {
s[++R] = c;
int cur = getFail(last);
if (nxt[cur][c] == 0) {
int now = newNode(len[cur] + 2);
fail[now] = nxt[getFail(fail[cur])][c];
nxt[cur][c] = now;
cont[now] = cont[fail[now]] + 1;
col[now] = col[cur] | (1 << c);
}
last = nxt[cur][c];
++cnt[last];
}
void count() {
for (int i = p - 1; i > 1; --i)
cnt[fail[i]] += cnt[i];
}
int main() {
string s;
cin >> s;
init(s.size());
for (auto c : s)
add(c - 'a');
count();
long long ans = 0;
for (int i = 0; i < p; ++i)
ans += 1ll * __builtin_popcount(col[i]) * cnt[i];
cout << ans << '\n';
}
H
第一次写min25筛,学了一波,发现就是一个奇妙的筛法然后合并和一些贡献然后每次可以一起算。
在这道题的话就是
∑
i
=
1
n
f
(
i
!
)
=
∑
p
i
s
p
r
i
m
e
∑
p
k
≤
n
(
n
+
1
)
[
n
p
k
]
−
p
k
C
(
[
n
p
]
+
1
,
2
)
\sum_{i = 1}^n f(i!)=\sum_{p \ is \ prime} \sum_{p^k \leq n} (n +1) [\frac{n}{p^k}] - p^k C([\frac{n}{p}] +1, 2)
i=1∑nf(i!)=p is prime∑pk≤n∑(n+1)[pkn]−pkC([pn]+1,2)
那么对于
k
≥
2
k \geq 2
k≥2时,直接暴力算贡献,对于
k
=
1
k=1
k=1时,可以min25处理素数个数和素数和即可。
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
using ll = long long;
int const M = 200005;
int const mod = 998244353;
int const inv2 = (mod + 1) / 2;
int const LIM = 100000;
ll id1[M], id2[M], w[M << 2], f[M << 1], g[M << 1], cnt, s[M];
ll n;
bool isp[M];
int pri[M], tot;
int get_id(ll x) {
if (x < LIM)
return id1[x];
return id2[n / x];
}
void SAI() {
isp[1] = 1;
for (int i = 2; i < LIM; ++i) {
if (isp[i] == 0) {
pri[++tot] = i;
s[tot] = (s[tot - 1] + i) % mod;
}
for (int j = 1; j <= tot && pri[j] * i < LIM; ++j) {
isp[pri[j] * i] = 1;
if (i % pri[j] == 0)
break;
}
}
}
ll C2(ll x) {
x %= mod;
return x * (x + 1) % mod * inv2 % mod;
}
int main() {
cin >> n;
SAI();
for (ll l = 1, r; l <= n; l = r + 1) {
ll t = n / l;
r = n / t;
w[++cnt] = t;
f[cnt] = t - 1;
g[cnt] = C2(t) - 1;
if (t < LIM)
id1[t] = cnt;
else
id2[r] = cnt;
}
for (int j = 1; j <= tot; ++j)
for (int i = 1; i <= cnt && 1ll * pri[j] * pri[j] <= w[i]; ++i) {
ll t = w[i] / pri[j];
int k = get_id(t);
f[i] = f[i] - f[k] - 1 + j;
g[i] = (g[i] + mod - 1ll * pri[j] * (g[k] - s[j - 1]) % mod) % mod;
}
ll ans = 0;
for (int i = 1; i <= tot; ++i)
for (ll j = 1ll * pri[i] * pri[i]; j <= n; j = j * pri[i])
ans = (ans + ((n + 1) % mod) * ((n / j) % mod) % mod - (j % mod) * C2(n / j) % mod + mod) % mod;
for (ll l = 2, r; l <= n; l = r + 1) {
r = n / (n / l);
ll t = n / l;
t %= mod;
ans = (ans + ((n + 1) % mod) * t % mod * ((f[get_id(r)] - f[get_id(l - 1)] + mod) % mod) % mod) % mod;
ans = (ans + 1ll * mod - 1ll * C2(t) * ((g[get_id(r)] - g[get_id(l - 1)] + mod) % mod) % mod) % mod;
}
cout << (ans + mod) % mod << '\n';
}
I
考虑把答案拆成
[
1
,
r
]
[1,r]
[1,r]和
[
1
,
l
]
[1,l]
[1,l]的答案差,然后再减去
[
1
,
l
]
[1,l]
[1,l]对于
[
1
,
r
]
[1,r]
[1,r]的影响。
那就树状数组统计一下二维偏序就好了。
#include <iostream>
#include <algorithm>
#include <vector>
struct Que {
int l, r;
int idx, ans;
};
int main () {
std::ios::sync_with_stdio(false);
int n, m;
std::cin >> n >> m;
auto v = std::vector<int>(n);
auto from = std::vector<int>(n + 1);
for (int i = 0; i < n; ++i) {
std::cin >> v[i];
from[v[i]] = i;
}
//auto sum1 = std::vector<int>(n);
auto sum2 = std::vector<int>(n + 1);
auto presum = [&](int x) {
int ret = 0;
for (++x; x; x -= x & -x)
ret += sum2[x];
return ret;
};
auto add = [&](int x, int dt) {
for (++x; x <= n; x += x & -x)
sum2[x] += dt;
};
auto tos = std::vector<std::vector<int>>(n);
for (int i = 1; i <= n; ++i)
for (int j = i * 2; j <= n; j += i) {
int a = from[i];
int b = from[j];
if (a > b)
std::swap(a, b);
tos[a].push_back(b);
//++sum1[b];
add(b, 1);
}
//for (int i = 1; i < n; ++i)
// sum1[i] += sum1[i - 1];
auto q = std::vector<Que>(m);
for (int i = 0; i < m; ++i) {
std::cin >> q[i].l >> q[i].r;
--q[i].l;
--q[i].r;
q[i].idx = i;
}
std::sort(std::begin(q), std::end(q), [](const Que &a, const Que &b) {
return a.l < b.l;
});
int idx = 0;
for (int l = 0; l < n; ++l) {
//std::cerr << "l=" << l << '\n';
for (; idx < m && q[idx].l == l; ++idx) {
q[idx].ans = presum(q[idx].r);
//std::cerr << "q[" << idx << "]=" << q[idx].ans << '\n';
}
for (int r: tos[l]) {
add(r, -1);
//std::cerr << "l=" << l << " add r=" << r << '\n';
}
}
std::sort(std::begin(q), std::end(q), [](const Que &a, const Que &b) {
return a.idx < b.idx;
});
for (auto &c: q)
std::cout << c.ans << '\n';
return 0;
}
J
比赛的时候傻逼了。以为可以很方便的计算反面,结果漏了一些贡献。
我们直接设
f
i
f_i
fi为
i
i
i这个子树到最深处的概率,就可以很方便的转移了。
哎白送了一题。
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <cstring>
using namespace std;
using ll = long long;
int const N = 1000005;
ll const mod = 1e9 + 7;
ll KSM(ll a, ll k) {
if (a < 0)
a = (a % mod + mod) % mod;
ll ret = 1;
if (k == -1)
k = mod - 2;
for (; k; k >>= 1, a = 1ll * a * a % mod)
if (k & 1)
ret = 1ll * ret * a % mod;
return ret;
}
int dep[N];
int mx[N];
int n;
ll du[N];
bool is[N];
int fa[N];
ll ans;
int f[N];
struct edge {
int y, next;
} e[N << 1];
int last[N], ne;
void addedge(int x, int y) {
e[++ne] = { y, last[x] };
last[x] = ne;
}
void pre_dfs(int x, int pre) {
fa[x] = pre;
dep[x] = dep[fa[x]] + 1;
mx[x] = dep[x];
for (int i = last[x]; i != 0; i = e[i].next) {
if (e[i].y == pre)
continue;
pre_dfs(e[i].y, x);
du[x]++;
mx[x] = max(mx[x], mx[e[i].y]);
}
}
void dfs(int x) {
if (du[x] == 0) {
if (dep[x] == mx[1])
f[x] = 1;
else
f[x] = 0;
return;
}
ll sum = 0;
for (int i = last[x]; i != 0; i = e[i].next) {
if (e[i].y == fa[x])
continue;
dfs(e[i].y);
sum += f[e[i].y];
}
sum = sum * KSM(du[x], -1);
sum = 1 - sum;
f[x] = mod + 1 - KSM(sum, du[x]);
f[x] %= mod;
}
int main() {
ios::sync_with_stdio(0);
cin >> n;
for (int i = 1; i < n; ++i) {
int x, y;
cin >> x >> y;
addedge(x, y);
addedge(y, x);
}
pre_dfs(1, 0);
dfs(1);
cout << f[1] << '\n';
}
K
#include <iostream>
#include <vector>
#include <map>
#include <algorithm>
int main () {
std::ios::sync_with_stdio(false);
int n;
std::cin >> n;
auto pts = std::vector<std::pair<int, int>>(n);
for (auto &p: pts)
std::cin >> p.first >> p.second;
auto mp = std::map<std::pair<int, int>, int>();
for (int i = 0; i < n; ++i)
for (int j = i + 1; j < n; ++j) {
auto mid = std::make_pair(pts[i].first + pts[j].first, pts[i].second + pts[j].second);
mp[mid] -= 2;
}
for (auto &p: pts)
mp[std::make_pair(p.first * 2, p.second * 2)] -= 1;
int mn = 0;
for (auto &it: mp)
mn = std::min(mn, it.second);
std::cout << n + mn << '\n';
return 0;
}
L
调参党的胜利x
最后时刻随便改了下参数过了。。。。。
显然你考虑一下每个地方去到另一个地方的最少翻转次数,那么就可以直接DP哈密顿回路了。
#include <iostream>
#include <vector>
#include <algorithm>
using P = std::pair<int, int>;
int len (P const &a, P const &b) {
int dx = std::abs(a.first - b.first);
int dy = std::abs(a.second - b.second);
if (dx > dy)
std::swap(dx, dy);
if (dx >= 2)
return dx + dy;
else if (dx == 1) {
if (dy >= 2) {
if (dy % 4 == 0)
return dx + dy;
else if (dy % 4 == 2)
return dx + dy + 2;
else
return dx + dy + 2;
} else { // dx == 1
return 6;
}
} else { // dx == 0
if (dy >= 2) {
if (dy % 4 == 0)
return dx + dy;
else
return dx + dy + 2;
} else if (dy == 1) {
return 3;
} else { // dy == 0
return 0;
}
}
}
const int N = 17;
int f[1 << N][N];
int main () {
std::ios::sync_with_stdio(false);
int t;
std::cin >> t;
while (t--) {
int n;
std::cin >> n;
auto v = std::vector<P>(n);
for (auto &c: v)
std::cin >> c.first >> c.second;
for (int rest = 1; rest < (1 << n); ++rest)
for (int beg = 0; beg < n; ++beg)
f[rest][beg] = 0x7fffffff;
for (int beg = 0; beg < n; ++beg)
f[0][beg] = 0;
for (int rest = 1; rest < (1 << n); ++rest)
for (int beg = 0; beg < n; ++beg)
for (int i = 0; i < n; ++i)
if (rest & (1 << i)) {
f[rest][beg] = std::min(
f[rest][beg],
f[rest ^ (1 << i)][i] + len(v[beg], v[i])
);
}
int ans = 0x7fffffff;
for (int i = 0; i < n; ++i)
ans = std::min(ans, f[(1 << n) - 1][i] + len({ 0, 0 }, v[i]));
std::cout << ans << '\n';
}
return 0;
}
M
被队友灌输了假题意,子序列变成子串,写了好多版本的代码都WA(题目都错了还想AC?
什么SAM,后缀树,exkmp啥的都写了一遍x
最后都exkmp了,觉得不可能啊,这怎么还会WA,我去看了下题x,发现了真相。
一个序列自动机就完事了。
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
using namespace std;
int const N = 1000005;
int nxt[N][26];
char s[N], t[N];
int n, m;
int main() {
ios::sync_with_stdio(0);
cin >> n >> m >> (s + 1) >> (t + 1);
for (int i = 0; i < 26; ++i)
nxt[n + 1][i] = n + 1;
for (int i = n; i >= 1; --i) {
for (int j = 0; j < 26; ++j)
nxt[i][j] = nxt[i + 1][j];
nxt[i][s[i] - 'a'] = i;
}
int p = 0, ans = -1;
for (int i = 1; i <= m; ++i) {
for (int j = t[i] - 'a' + 1; j < 26; ++j)
if (nxt[p + 1][j] <= n)
ans = max(ans, n - nxt[p + 1][j] + i);
p = nxt[p + 1][t[i] - 'a'];
if (p == n + 1)
break;
}
if (p < n)
ans = max(ans, n - p + m);
cout << ans << '\n';
}