CF 1900+ 题板刷记
想提高 cf rating 还是得突破自己的瓶颈,分类做下一些题。
1527D MEX Tree
题意:给出一棵 \(n(2\le n\le2\cdot10^5)\) 个节点的树,对 \(k\) 从 \(0\) 到 \(n\),求无序对 \((u,v),u\neq v\) 的数量使得从 \(u\) 到 \(v\) 的最短路径上节点编号的 MEX 是 \(k\)。
先求累加和再做差分的思想。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <vector>
using namespace std;
#define debug(x) cout << #x << " is " << x << endl
typedef long long ll;
typedef pair<int, int> P;
const int INF = 0x3f3f3f3f, N = 2e5 + 5;
int t, n, p, q, flag;
int sz[N], fa[N], vis[N];
ll ans[N];
vector<int> G[N];
void add(int u) {
if (vis[u]) return;
if (!vis[fa[u]]) add(fa[u]);
vis[u] = 1;
if (fa[u] != p && fa[u] != q) { flag = 0; return; }
else {
if (fa[u] == p) p = u;
else q = u;
if (fa[u] == 0) sz[0] -= sz[u];
}
}
void dfs(int u, int f) {
sz[u] = 1; fa[u] = f;
for (int v : G[u]) {
if (v == f) continue;
dfs(v, u);
sz[u] += sz[v];
}
}
int main()
{
scanf("%d", &t);
while (t--) {
scanf("%d", &n);
for (int i = 0; i <= n; ++i) sz[i] = fa[i] = ans[i] = vis[i] = 0, G[i].clear();
int x, y;
for (int i = 1; i < n; ++i) {
scanf("%d%d", &x, &y);
G[x].push_back(y);
G[y].push_back(x);
}
dfs(0, -1);
int sum = 0;
for (int u : G[0]) {
ans[0] += 1ll * sz[u] * (sz[u] - 1) / 2;
sum += sz[u];
ans[1] += 1ll * sz[u] * (sz[0] - sum);
}
flag = 1; p = q = 0; vis[0] = 1;
for (int i = 1; i < n; ++i) {
add(i); // 加入 i 后找出路径端点 p, q
if (!flag) break; // 不是链
ans[i] -= 1ll * sz[p] * sz[q];
ans[i+1] = 1ll * sz[p] * sz[q];
}
for (int i = 0; i <= n; ++i) printf("%lld%c", ans[i], " \n"[i == n]);
}
return 0;
}
1499D The Number of Pairs
题意: 给出 \(c,d,x(1\le c,d,x\le10^7)\) 求满足 \(c\cdot\mathrm{lcm}(a,b)-d\cdot\gcd(a,b)=x\) 的 \((a,b)\)。
设 \(c=Ag,d=Bg\),则 \(\mathrm{lcm}(c,d)=ABg,\gcd(c,d)=g\),\(AB=\displaystyle\frac{\frac{x}{g}+d}{c}\) (记为 \(k(k\le2\cdot10^7)\)),且 \((A,B)=1\),记 \(k\) 的素因子有 \(res\) 个,那么易知最后答案为 \(2^{res}\),预处理素因子(改下线性筛)。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
#define debug(x) cout << #x << " is " << x << endl
#define inc(i, a, b) for (int i = a; i <= b; ++i)
typedef long long ll;
const int INF = 0x3f3f3f3f, N = 2e7 + 5;
int t, c, d, e, cnt;
int v[N], pri[N], bin[35], num[N];
void init() {
bin[0] = 1;
for (int i = 1; i <= 30; ++i)
bin[i] = bin[i-1] << 1;
for (int i = 2; i < N; ++i) {
if (!v[i]) { v[i] = i; pri[++cnt] = i; num[i] = 1; }
for (int j = 1; j <= cnt && pri[j] * i < N; ++j) {
v[pri[j]*i] = pri[j];
num[i*pri[j]] = num[i] + (v[i] != pri[j]);
if (i % pri[j] == 0) break;
}
}
}
int get(int c, int d, int k) {
if ((k + d) % c) return 0;
return bin[num[(k+d)/c]];
}
int main()
{
scanf("%d", &t);
init();
while (t--) {
scanf("%d%d%d", &c, &d, &e);
ll ans = 0;
for (int i = 1; i * i <= e; ++i) {
if (e % i) continue;
ans += get(c, d, i);
if (i != e / i) ans += get(c, d, e / i);
}
printf("%lld\n", ans);
}
return 0;
}
1495B Let's Go Hiking
考虑最长单调区间长度 \(mx(mx\ge2)\) 和数量 \(cnt(cnt\ge1)\)。
- \(cnt\gt2\) 时,Qingshan 最多移动 \(mx-1\) 次,但 Daniel 总能找到位置移动 \(mx-1\) 次。
- \(cnt=1\) 时,不妨设最长单调序列 \(p_s,p_{s+1}\cdots,p_{s+mx-1}\) 是递增的,Qingshan 一开始必取 \(x=s+l-1\),讨论 \(mx\) 如下:
- 如果 \(mx\) 为偶数,Daniel 可以一开始选择 \(y=s\),第 \(mx-3\) 局 (Qingshan 的局) \(x=s+mx-\frac{mx}{2}\),第 \(mx-2\) 局 (Daniel 的局) \(y=s+\frac{mx}{2}-1\),下一局 Qingshan 失败。
- 如果 \(mx\) 为奇数,Daniel 一开始选择 \(y=s+1\) 转换为与上类似居面,Qingshan 失败。
- \(cnt=2\) 时,两个最长单调区间必须类似 \(p_{m-mx+1}\lt p_{m-mx+2}\lt\cdots\lt p_m\gt p_{m+1}\gt\cdots\gt p_{m+mx-1}\) (否则 Qingshan 必败),综合上面的讨论, \(mx\) 为偶数 Qingshan 必败 (Daniel 一开始选择 \(y=m-mx+1\)),\(mx\) 为奇数 Qingshan 必胜。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
#define debug(x) cout << #x << " is " << x << endl
#define inc(i, a, b) for (int i = a; i <= b; ++i)
typedef long long ll;
const int INF = 0x3f3f3f3f, N = 1e5 + 5;
int n;
int p[N], d[N];
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; ++i) scanf("%d", &p[i]);
for (int i = 2; i <= n; ++i) d[i] = (p[i-1] < p[i]);
int mx = 0, cnt = 0;
for (int l = 2, r = 2; l <= n; l = r = r + 1) {
while (r + 1 <= n && d[l] == d[r+1]) ++r;
int len = r - l + 2;
if (len > mx) mx = len, cnt = 1;
else if (len == mx) ++cnt;
}
if (cnt != 2 || !(mx & 1)) return puts("0"), 0;
int flag = 0;
for (int i = 3; i <= n; ++i) {
if (d[i-1] && !d[i]) {
int l = i - 1, r = i;
while (l > 2 && d[l-1]) --l;
while (r < n && !d[r+1]) ++r;
if ((i - 1) - (l - 1) + 1 == mx && r - (i - 1) + 1 == mx) {
flag = 1; break;
}
}
}
printf("%d\n", flag);
return 0;
}
1490G Old Floppy Drive
1415D XOR-gun
posted by 2inf