[赛记] NOIP2024加赛2
新的阶乘 100pts
当发现直接做不好做时,可以从贡献的角度考虑;
那么对于一个质数,我们只需要算出它的所有贡献,直接暴力算即可;
复杂度和埃氏筛差不多,
点击查看代码
#include <iostream>
#include <cstdio>
using namespace std;
int n;
long long ans[10000005], an[10000005];
long long pri[10000005];
bool vis[10000005];
int cnt;
void p() {
for (int i = 2; i <= n; i++) {
if (!vis[i]) {
pri[++cnt] = i;
for (int j = 2; j * i <= n; j++) {
vis[j * i] = true;
}
}
}
}
int main() {
freopen("factorial.in", "r", stdin);
freopen("factorial.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n;
p();
for (int i = 1; i <= cnt; i++) {
an[pri[i]] = 1;
for (int j = 1; j * pri[i] <= n; j++) {
ans[pri[i]] += (an[j] + 1) * (n - (j * pri[i] - 1));
an[j * pri[i]] = an[j] + 1;
}
an[pri[i]] = 0;
for (int j = 1; j * pri[i] <= n; j++) {
an[j * pri[i]] = 0;
}
}
cout << "f(" << n << ")=";
int pos = 0;
for (int i = 1; i <= n; i++) {
if (ans[i]) pos = i;
}
for (int i = 1; i <= n; i++) {
if (ans[i]) {
if (i != pos) {
if (ans[i] == 1) cout << i << "*";
else cout << i << "^" << ans[i] << "*";
} else {
if (ans[i] == 1) cout << i;
else cout << i << "^" << ans[i];
}
}
}
return 0;
}
博弈树 0pts
考虑这玩意会和直径有关,Bob能赢当且仅当直径所包含的点数为奇数且这个点为直径中点,否则不管Bob怎么走,Alice总能复制Bob的行走路线走到以直径中点为对称中心的对称点,最后将Bob逼到直径的一个端点处而取得胜利;
于是找直径即可,时间复杂度:
点击查看代码
#include <iostream>
#include <cstdio>
using namespace std;
int n, q;
struct sss{
int t, ne;
}e[500005];
int h[500005], cnt;
void add(int u, int v) {
e[++cnt].t = v;
e[cnt].ne = h[u];
h[u] = cnt;
}
int dis[500005], fa[500005];
void dfs(int x, int f) {
dis[x] = dis[f] + 1;
fa[x] = f;
for (int i = h[x]; i; i = e[i].ne) {
int u = e[i].t;
if (u == f) continue;
dfs(u, x);
}
}
int main() {
freopen("tree.in", "r", stdin);
freopen("tree.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> q;
int x, y;
for (int i = 1; i <= n - 1; i++) {
cin >> x >> y;
add(x, y);
add(y, x);
}
dfs(1, 0);
int ma = 0, pos = 0;
for (int i = 1; i <= n; i++) {
if (dis[i] > ma) {
pos = i;
ma = dis[i];
}
}
for (int i = 1; i <= n; i++) dis[i] = 0;
dfs(pos, 0);
ma = 0;
int po = 0;
for (int i = 1; i <= n; i++) {
if (dis[i] > ma) {
po = i;
ma = dis[i];
}
}
ma--;
int ans = 0;
if (!(ma & 1)) {
while(po != pos) {
if (dis[po] - 1 == ma / 2) {
ans = po;
break;
}
po = fa[po];
}
}
for (int i = 1; i <= q; i++) {
cin >> x;
if (n == 1) {
cout << "Bob" << '\n';
continue;
}
if (x == ans) cout << "Bob" << '\n';
else cout << "Alice" << '\n';
}
return 0;
}
划分 9pts
这个题很容易想到DP,且很容易写,然后就得了9pts;
问题在于取模以后无法比较大小;
所以正解不是DP;
我们不难发现,最后分的段越少越好,但是我们要分以下几种情况讨论;
- 前
位全是 ;
将后面从
;
方案数为
- 全是
;
方案数直接插板,答案为
- 普通情况;
发现将原序列分成一个长度为
我们发现,对于那个长度为
那么我们用最大表示法找出来最大的
然后简单统计一下答案即可(不要忘了最大的
时间复杂度:
点击查看代码
#include <iostream>
#include <cstdio>
using namespace std;
const long long mod = 998244353;
const long long pep = 229;
int n, k;
char s[5000005];
long long fac[5000005], fav[5000005];
long long h[5000005], p[5000005];
long long ksm(long long a, long long b) {
long long ans = 1;
while(b) {
if (b & 1) ans = ans * a % mod;
a = a * a % mod;
b >>= 1;
}
return ans;
}
long long C(long long a, long long b) {
if (a < b) return 0;
return fac[a] * fav[b] % mod * fav[a - b] % mod;
}
int main() {
freopen("divide.in", "r", stdin);
freopen("divide.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> k;
cin >> (s + 1);
fac[0] = 1;
fav[0] = 1;
for (int i = 1; i <= n; i++) {
fac[i] = fac[i - 1] * i % mod;
fav[i] = ksm(fac[i], mod - 2);
}
if (n == k) {
long long ans = 0;
for (int i = 1; i <= n; i++) {
ans += (s[i] == '1');
}
cout << ans << ' ' << 1;
return 0;
}
int pos = 0;
for (int i = 1; i <= n; i++) {
if (s[i] == '1') {
pos = i;
break;
}
}
if (pos > k) {
long long ans = 0;
for (int i = k - 1; i <= pos - 1; i++) {
ans = (ans + C(pos - 1, i)) % mod;
}
long long sum = 0;
for (int i = n; i >= pos; i--) {
if (s[i] == '1') {
sum = (sum + ksm(2, n - i)) % mod;
}
}
cout << sum << ' ' << ans;
return 0;
}
int i = 1, j = 2;
while(i <= k && j <= k) {
int x = 0;
for (x = 0; x <= n - k - 1; x++) {
if (s[i + x] != s[j + x]) break;
}
if (s[i + x] < s[j + x]) {
i += (x + 1);
} else {
j += (x + 1);
}
if (i == j) j++;
}
pos = min(i, j);
h[1] = s[1];
p[0] = 1;
p[1] = pep;
for (int i = 2; i <= n; i++) {
h[i] = (h[i - 1] * pep % mod + s[i]) % mod;
p[i] = p[i - 1] * pep % mod;
}
long long sum = ((h[pos + n - k - 1] - h[pos - 1] * p[n - k] % mod) + mod) % mod;
long long ans = 0;
for (int i = 1; i + n - k - 1 <= n - 1; i++) {
if ((h[i + n - k - 1] - h[i - 1] * p[n - k] % mod + mod) % mod == sum) ans++;
}
sum = 0;
long long ss = 0;
for (int i = pos + n - k - 1; i >= pos; i--) {
if (s[i] == '1') {
sum = (sum + ksm(2, (pos + n - k - 1) - i + 1)) % mod;
ss++;
}
}
long long su = 0;
for (int i = 1; i <= n; i++) su += (s[i] == '1');
if (su == 0) {
sum = 0;
for (int i = k - 1; i <= n - 1; i++) {
sum = (sum + C(n - 1, i)) % mod;
}
cout << 0 << ' ' << sum;
return 0;
}
cout << sum + su - ss << ' ' << ans;
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?