2024.10.16 CW 模拟赛
题目链接:https://files.cnblogs.com/files/blogs/833333/CW1016.zip?t=1729123549&download=true
T2 (CF1101D)
算法:\(dfs\) 求直径,\(MIller\_Rabin\) 判断素数(可用可不用,但不用会 \(wrong\ answer\) ,不知道为什么
阅读题目,旨在让我们求一条 \(gcd\) 不等于 \(1\) 的一条最长链。\
容易知道如果一条链上所有数的 \(gcd\) 不为一,那么其一定有共同的质因数。\
所以可以想到把有相同质因子的点放在一起处理,再在每个连通块里面求一次树的直径就行了。\
最后如果点权全是 \(1\) ,须要特判输出 \(0\) 。
CODE
#include "iostream"
#include "cstdio"
#include "vector"
#include "bits/extc++.h"
using namespace std;
using namespace __gnu_pbds;
// char buf[1 << 20], *p1, *p2;
// #define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 20, stdin), p1 == p2) ? 0 : *p1++)
template <typename T>
inline void read(T &x)
{
bool f = 1;
x = 0;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
f = !f;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
x = (f ? x : -x);
return;
}
template <typename T>
inline void write(T x)
{
if (x < 0)
putchar('-'), x = -x;
if (x > 9)
write(x / 10);
putchar(x % 10 + '0');
return;
}
const int N = 2e5 + 10;
int cnt = 0, p[N];
bool is_prime[N];
inline void calculate_prime()
{
is_prime[1] = 1;
for (int i = 2; i <= 50000; ++i)
{
if (!is_prime[i])
p[++cnt] = i;
for (int j = 1; j <= cnt and p[j] * i <= 50000; ++j)
{
is_prime[p[j] * i] = 1;
if (!(i % p[j]))
break;
}
}
return;
}
int n, a[N], dis[N], q[N];
int z[N];
bool vis[N];
int tail;
int Mod, A[11] = {0, 2, 3, 5, 7, 11, 13, 17, 19, 23, 61};
vector<int> tr[N];
cc_hash_table<int, vector<int>> mp;
cc_hash_table<int, int> t;
int quickpower(int x, int y)
{
int ans = 1, op = x;
while (y)
{
if (y & 1)
ans *= op, ans %= Mod;
op *= op, op %= Mod;
y >>= 1;
}
return ans;
}
inline bool judge_prime(int x)
{
Mod = x;
int s = 0;
if (x == 2)
return 1;
if (x % 2 == 0 || x == 1)
return 0;
int p = x - 1;
while (p % 2 == 0)
p /= 2, s++;
for (int j = 1; j <= 5; j++)
{
int B = quickpower(A[j], p);
for (int i = 1; i <= s; i++)
{
int K = (B * B) % Mod;
if (K == 1ll && B != 1ll && B != Mod - 1)
return 0;
B = K;
}
if (B != 1ll)
return 0;
}
return 1;
} //不知为什么不用这个会错,故加上
void dfs(int nw, int prime, int fa)
{
vis[nw] = 1;
q[++tail] = nw;
dis[nw] = dis[fa] + 1;
for (int to : tr[nw])
{
if (to == fa)
continue;
if (!(a[to] % prime))
dfs(to, prime, nw);
}
return;
}
inline int calculate_longest(int nw, int prime)
{
tail = 0;
int ans = 0;
vis[nw] = 1;
dfs(nw, prime, 0);
int f = 0;
for (int i = 1; i <= tail; ++i)
if (dis[q[i]] > dis[f])
f = q[i];
for (int i = 1; i <= tail; ++i)
dis[q[i]] = 0;
tail = 0;
dfs(f, prime, 0);
f = 0;
for (int i = 1; i <= tail; ++i)
if (dis[q[i]] > dis[f])
f = q[i];
return dis[f];
}
int main()
{
calculate_prime();
read(n);
for (int i = 1; i <= n; ++i)
read(a[i]);
for (int i = 1; i < n; ++i)
{
int u, v;
scanf("%d %d", &u, &v);
tr[u].push_back(v);
tr[v].push_back(u);
}
cnt = 0;
for (int i = 1; i <= n; ++i)
{
int x = a[i];
bool fl = judge_prime(x);
if (fl)
{
mp[x].push_back(i);
if (t[x] != 1)
t[x] = 1, z[++cnt] = x;
continue;
}
for (int j = 1; p[j] * p[j] <= x; ++j)
while (!(x % p[j]))
{
x /= p[j], mp[p[j]].push_back(i);
if (!t[p[j]])
t[p[j]] = 1, z[++cnt] = p[j];
}
if (x != 1)
{
mp[x].push_back(i);
if (t[x] != 1)
t[x] = 1, z[++cnt] = x;
}
}
int ans = 1;
for (int i = 1; i <= cnt; ++i)
{
for (int x : mp[z[i]])
if (!vis[x])
ans = max(ans, calculate_longest(x, z[i]));
for (int x : mp[z[i]])
vis[x] = dis[x] = 0;
}
for (int i = 1; i <= n; ++i)
if (a[i] != 1)
return write(ans), 0;
write(0);
return 0;
}
T3 (CF962E)
一道构造题。
画画图可以发现,无非就两种决策:
- 1.\(P,B\ or\ P,R\) 之间依次连边
- 2.相邻的 \(P\) 之间连边(作为 \(B\)、\(R\) 与 \(P\) 连通图的公用边)
最后相邻的 \(P\) 之间 \(B\) 和 \(R\) 单独连边。(中间一条最大边可以去掉)
#include "iostream"
using namespace std;
#define int long long
int n, ans;
int d = 0, p = 0, f = 0, v = 0, g = 0;
bool flr = 0, flb = 0, flp = 0;
signed main()
{
cin >> n;
for (int i = 1; i <= n; i++)
{
int x;
char c;
cin >> x >> c;
if (c == 'P' or c == 'R')
{
if (flr)
ans += (x - d), p = max(p, x - d);
flr = 1, d = x;
}
if (c == 'P' or c == 'B')
{
if (flb)
ans += (x - f), v = max(v, x - f);
flb = 1, f = x;
}
if (c == 'P')
{
if (flp)
ans += min(0ll, x - g - v - p);
flp = 1, p = v = 0, g = x;
}
}
cout << ans << endl;
return 0;
}
T4
正解:最短路,状压 \(dp\)
- 对于20%的数据,\(n \le 300,k \le 10\)
将起点与特殊点都称为关键点
用 \(Floyd\) 计算出关键点之间及每个关键点到任意一个终点的最小距离,然后搜索即可
复杂度 \(\mathcal{O}(n^3+k!)\) 。
-
对于50%的数据,\(n \le 1000,k \le 10\)
改用 \(dijkstra\) 算法,枚举所有关键点为起点的单源最短路 \(\mathcal{O}(kn^2+k!)\) -
对于70%的数据,\(k \le 20\)
考虑状压 \(dp\).
令 \(f_{i,S}\) 表示当前经过的关键点集合为 \(S\) ,最终到达 \(i\) 点进过的最短距离, 用二进制数表示集合(状态压缩).
复杂度 \(\mathcal{O}(km \log m+k^2*2^k)\).
#include "iostream"
#include "cstdio"
#include "cstring"
#include "vector"
#include "queue"
using namespace std;
#define lowbit(x) (x & -x)
typedef pair<int, int> pii;
typedef long long ll;
template <typename T>
inline void read(T &x)
{
bool f = 1;
x = 0;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
f = !f;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
x = (f ? x : -x);
return;
}
const int N = 5e4 + 10, M = 20;
int n, m, k, s;
int d[N], p[N];
vector<pii> g[N]; // 输入
ll dis[N], f[M][1 << M];
ll a[M][M], v[M], sz[1 << M];
bool vis[N];
deque<int> q;
inline void spfa(int s)
{
for (int i = 1; i <= n; ++i)
dis[i] = 1e18, vis[i] = 0;
dis[s] = 0;
q.push_back(s);
vis[s] = 1;
while (!q.empty())
{
int nw = q.front();
q.pop_front();
vis[nw] = 0;
for (auto to : g[nw])
{
if (dis[to.first] > dis[nw] + to.second)
{
dis[to.first] = dis[nw] + to.second;
if (!vis[to.first])
{
if (!q.empty() and dis[to.first] < dis[q.front()])
q.push_front(to.first);
else
q.push_back(to.first);
vis[to.first] = 1;
}
}
}
}
return;
}
int main()
{
read(n), read(m), read(k), read(s);
for (int i = 1; i <= n; ++i)
read(p[i]);
for (int i = 1; i <= m; ++i)
{
int u, v, w;
read(u), read(v), read(w);
g[u].emplace_back(v, w);
g[v].emplace_back(u, w);
}
for (int i = 0; i < k; ++i)
read(d[i]);
for (int i = 0; i < k; ++i)
{
spfa(d[i]);
f[i][1 << i] = dis[s];
v[i] = 1e18;
for (int j = 1; j <= n; ++j)
if (p[j])
v[i] = min(v[i], dis[j]);
for (int j = 0; j < k; ++j)
a[i][j] = dis[d[j]];
}
for (int w = 1; w < (1 << k); ++w)
{
sz[w] = sz[w - lowbit(w)] + 1;
if (!(w - lowbit(w)))
continue;
for (int i = 0; i < k; ++i)
{
f[i][w] = 1e18;
if (!((w >> i) & 1))
continue;
for (int j = 0; j < k; ++j)
if (i != j and (w >> j) & 1)
f[i][w] = min(f[i][w], f[j][w - (1 << i)] + a[i][j] * sz[w]);
}
}
ll ans = 1e18;
for (int i = 0; i < k; ++i)
ans = min(ans, f[i][(1 << k) - 1] + v[i] * (k + 1));
printf("%lld", ans);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现