NOIP 膜你赛 做题记录
题目大意:
定义
现在小
-
。 -
如果正整数
满足 ,则 。 -
如果正整数
满足 ,且存在正整数
满足 ,则 。
现在小
输入格式
一行三个正整数
输出格式
一行一个整数表示答案。
样例输入
20 8 2
样例输出
7
样例解释
数据范围
对于所有数据,
对于前
对于前
对于前
思路
这题正解应该是考虑图论意义,将
写个程序跑出来大概是
但我只会证明它严格小于
所以边数大概是
#include <bits/stdc++.h>
using namespace std;
const int N = 1e7 + 10, M = 2e8 + 10;
typedef long long ll;
int n, x, k, sq, t, ans;
int h[N], ne[M], e[M], idx;
int num[N];
queue<int> q;
bool vis[N];
inline void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
void bfs() {
q.push(x);
vis[x] = 1;
while(q.size()) {
t = q.front();
q.pop();
for (int i = h[t]; ~i; i = ne[i]) {
int j = e[i];
if (!vis[j]) {
vis[j] = true;
q.push(j);
}
}
}
}
int main() {
memset(h, -1, sizeof h);
scanf("%d%d%d", &n, &x, &k);
for (int i = 1; i <= n; i++)
num[i] = i % 10 + num[i / 10];
sq = sqrt(n);
for(int i = 2; i <= sq; i++)
for (ll j = 1ll * i * i; j <= n; j *= i)
add(j, i);
int cnt = 0;
for (int i = 1; i <= n; i++) {
t = i - num[i] * k;
if(t > 0) add(t, i);
}
bfs();
for (int i = 1; i <= n; i++)
vis[i] ? ans++ : 1;
printf("%d", ans);
return 0;
}
但考场上我写了一种玄学做法:开个 bool 数组,一直从前往后扫并根据规则更新那些数在集合中,若扫完后答案和扫之前相同,说明更新完毕,退出。
时间复杂度
#include <ctime>
#include <cmath>
#include <iostream>
using namespace std;
const int N = 10000010;
typedef long long ll;
int n, x, k;
int sum[N];
bool st[N];
int ans;
void check(int x) {
ll pow = 1ll * x * x;
while(pow <= n) {
if(st[pow]) {
st[x] = true;
ans++;
return ;
}
pow *= x;
}
}
void check2(int x) {
if(x - k * sum[x] >= 2 && st[x - k * sum[x]]) {
ans++;
st[x] = true;
}
}
int main() {
scanf("%d%d%d", &n, &x, &k);
if(x == 1) {
puts("1");
return 0;
}
for(int i = 1; i <= n; i++)
sum[i] = i % 10 + sum[i / 10];
st[x] = true;
ans++;
int limit = (int)sqrt(n);
//玄学
while(1) {
int las = ans;
for(int i = 2; i <= limit; i++)
if(!st[i])
check(i);
for(int i = 1; i <= n; i++)
if(!st[i])
check2(i);
if(ans == las) break;
}
printf("%d\n", ans);
return 0;
}
题目描述
棱镜宫殿的大门前有一个奇妙的装置。这个装置上有若干个转轮,每个转轮上有一个
小凯为了进入棱镜宫殿,他可以进行如下操作一次:
选择其中一些转轮转动,对于每个选择的转轮,操作之后会独立的等概率的变化成
如果操作后新的
输入格式
一行一个正整数
输出格式
输出一行一个整数,表示最大的概率。
样例输入
115
样例输出
970293512
样例解释
最优的操作是选择所有转轮,概率为
数据范围与约定
- 对于所有数据,有:
- 输入没有前导
思路
做一个简单的贪心:选择数值尽量小的改变,概率会尽量大。
令
采取这样一种策略:
- 选取
中数位最小且最靠前的那位,记为 ,操作它; - 令
; - 反复执行
直到 。
为什么这样是对的呢?
首先找最小值肯定是对的,但需要考虑怎么找。
先钦定好寻找顺序。
假设我们现在找到最靠前的最小值为一个数位上的数
设先操作
根据条件概率可得:
两者作差,得:
根据
所以从前往后选择是更优的。
令
这是一个字典序的形式,不断贪心的取最靠前的后缀最小值就行。
时间复杂度为
#include<bits/stdc++.h>
using namespace std;
const int N = 200010, mod = 998244353;
typedef long long ll;
int n;
char s[N];
ll invs[N];
int minn[N];
int pos[N];
ll inv(ll a, ll b = mod - 2) {
ll ans = 1, base = a % mod;
while(b) {
if(b & 1) ans = ans * base % mod;
base = base * base % mod;
b >>= 1;
}
return ans;
}
int main() {
// freopen("ex_opt3.in", "r", stdin);
// freopen("ans.out", "w", stdout);
scanf("%s", s + 1);
n = strlen(s + 1);
invs[1] = inv(10);
for(int i = 2; i <= n; i++)
invs[i] = invs[i - 1] * invs[1] % mod;
minn[n] = s[n] - '0';
pos[n] = n;
for(int i = n - 1; i; i--) {
minn[i] = min(minn[i + 1], s[i] - '0'); //求后缀最小值
if(minn[i + 1] >= s[i] - '0')
minn[i] = s[i] - '0', pos[i] = i;
else minn[i] = minn[i + 1], pos[i] = pos[i + 1];
}
int l = 1, cnt = 1;
ll res = 0;
while(l <= n) {
res = (res + 1ll * (9 - minn[l]) * invs[cnt]) % mod;
cnt++, l = pos[l] + 1;
}
printf("%lld\n", res);
return 0;
}
题目描述
有两个长度为
,以及一个数
茵蒂克丝想要求出区间
茵蒂克丝想要求出
输入格式
第一行四个数
第二行
第三行
接下来
设上一次询问的答案的分子为
输出格式
输出
如果不存在答案则输出
样例 1 输入
5 2 6 0
2 4 1 3 7
6 1 5 2 4
1 2
1 4
3 3
2 4
2 3
1 3
样例 1 输出
5/6
5/3
0/1
5/3
4/7
1/1
样例 2 输入
5 2 6 1
2 4 1 3 7
6 1 5 2 4
1 2
1 4
3 3
2 4
2 3
1 3
样例 2 输出
5/6
5/3
0/1
5/3
5/6
5/3
数据范围与约定
对于
需要注意,即使
思路:
首先考虑部分分,
设(pair类型的)
状态转移方程为:
状态数量为
然后注意到
引理:若
证明很简单,从溶液浓度方面来理解:一杯甜度为
所以最优区间的长度一定小于
这时有用的区间就只剩下
可以建立
回想区间 dp 的做法,发现两者可以做一个平衡,将区间长度小于等于
具体地,设
对于
时间复杂度为
#include <bits/stdc++.h>
#define x first
#define y second
using namespace std;
const int N = 1000010;
typedef long long ll;
typedef pair<int, int> PII;
int n, k, q, type;
ll suma[N], sumb[N];
PII dp[21][N];
int log_2[N];
void init_log_2() {
log_2[1] = 0;
for(int i = 2; i <= n; i++)
log_2[i] = log_2[i / 2] + 1;
}
inline PII cmp(PII a, PII b) {
if(!a.x && !a.y) return b;
if((ll)a.x * b.y < (ll)b.x * a.y) return b;
return a;
}
ll gcd(ll a, ll b) {
if(!b) return a;
return gcd(b, a % b);
}
PII f[21][N];
void init() {
for(int j = 1; j <= log_2[n]; j++)
for(int i = 1; i + (1 << j) - 1 <= n; i++)
f[j][i] = cmp(f[j - 1][i], f[j - 1][i + (1 << j - 1)]);
}
inline PII query(int l, int r) {
int t = log_2[r - l + 1];
return cmp(f[t][l], f[t][r - (1 << t) + 1]);
}
int main() {
scanf("%d%d%d%d", &n, &k, &q, &type);
init_log_2();
for(int i = 1; i <= n; i++) {
scanf("%d", &suma[i]);
suma[i] += suma[i - 1];
}
for(int i = 1; i <= n; i++) {
scanf("%d", &sumb[i]);
sumb[i] += sumb[i - 1];
}
for(int len = k; len <= k * 2; len++)
for(int l = 1; l + len - 1 <= n; l++) {
int r = l + len - 1;
int sa = suma[r] - suma[l - 1];
int sb = sumb[r] - sumb[l - 1];
PII tmp = {sa, sb};
if(len > k) dp[len - k][l] = cmp(cmp(dp[len - k - 1][l], dp[len - k - 1][l + 1]), tmp);
else dp[len - k][l] = tmp;
}
for(int i = 1; i <= n - k + 1; i++)
for(int j = i + k - 1; j <= min(i + k * 2 - 1, n); j++)
f[0][i] = cmp(f[0][i], {suma[j] - suma[i - 1], sumb[j] - sumb[i - 1]});
init();
int l, r, lasans = 0;
while(q--) {
scanf("%d%d", &l, &r);
l = (l ^ (lasans * type)) % n + 1, r = (r ^ (lasans * type)) % n + 1;
if(l > r) swap(l, r);
if(r - l + 1 < k) {
printf("0/1\n");
lasans = 0;
continue;
}
PII ans = {0, 0};
int limit = r - 2 * k + 1;
if(limit >= l) ans = query(l, limit);
else limit = l - 1;
ans = cmp(ans, dp[r - limit - k][limit + 1]);
int d = gcd(ans.x, ans.y);
ans.x /= d, ans.y /= d;
printf("%d/%d\n", ans.x, ans.y);
lasans = ans.x;
}
return 0;
}
题目描述
菜汪酱是天文学家,喜欢观察行星的运动。
菜汪酱发现所在星系的行星都会绕着恒星转动。一共有
通过对行星运动规律的长时间总结和归纳,菜汪酱发现行星会在一个以恒星为圆点的正圆形轨道上顺时针转动。并且通过一系列的观察,菜汪酱得到了所有行星的周期。更具体地,菜汪酱发现第
你可能听说过
菜汪酱认为出现这种情况是美丽的,因此想要知道发生
你只需要验算就可以了,所以只需要你输出对
当然结果可能并非整数,但是可以证明结果一定是有理数,因此如果最后的答案是
输入格式
第一行一个整数
接下来一行
输出格式
一行一个整数表示答案。
样例输入
2
114 514
样例输出
14649
数据范围
对于
对于
对于
思路:
很容易将原题面转化为:
给定
首先要声明一点,不能在做
知道了这一点后,要么使用高精度算
鉴于高精度除法和取模太难写,在考场上很难写对,所以学习优美的另一种算法。
维护
这里的
#include <iostream>
using namespace std;
typedef unsigned long long ull;
const int N = 5010, mod = 998244353, inv2 = (mod + 1) / 2;
int n;
ull a[N], b[N];
inline ull gcd(ull a, ull b) {
if(!b) return a;
return gcd(b, a % b);
}
int main() {
scanf("%d", &n);
ull x;
for(int i = 1; i <= n; i++) {
scanf("%llu", &x);
ull mul = 1;
for(int j = 1; j < i; j++)
mul = (__int128)mul * b[j] % x;
b[i] = x / gcd(mul, x);
}
ull res = 1;
for(int i = 1; i <= n; i++)
res = (__int128)res * b[i] % mod;
printf("%llu", res * inv2 % mod);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具