2024 寒假模拟赛题解
送分题,分解质因数,分别加入两个桶里,然后随便怎么乱搞比对一下就行。
代码:
#include <bits/stdc++.h>
#define int long long
#define mod 1000000000
using namespace std;
int n, m;
unordered_map<int, signed>mpa;
unordered_map<int, signed>mpb;
void solve(int x, bool flg) {
for (int i = 2; i * i <= x; i++) {
if (x % i == 0) {
if (flg)
while (x % i == 0)
mpa[i]++, x /= i;
else
while (x % i == 0)
mpb[i]++, x /= i;
}
}
if (x > 1) {
if (flg)
mpa[x]++;
else
mpb[x]++;
}
}
int qpow(int x, int y) {
int ans = 1;
while (y) {
if (y & 1)
ans = ans * x % mod;
x = x * x % mod;
y >>= 1;
}
return ans;
}
signed main() {
scanf("%lld", &n);
for (int i = 1; i <= n; i++) {
int x;
scanf("%lld", &x);
solve(x, 1);
}
scanf("%lld", &m);
for (int i = 1; i <= m; i++) {
int x;
scanf("%lld", &x);
solve(x, 0);
}
int ans = 1;
for (unordered_map<int, signed>::iterator it = mpa.begin(); it != mpa.end(); it++) {
int key = (*it).first, num = (*it).second;
if (mpb.count(key))
ans = (ans * qpow(key, min(num, (int)mpb[key])) % mod);
}
cout << ans << "\n";
return 0;
}
做法貌似很多,说一下我知道的几种。
-
预处理,算出每个横纵坐标各有多少个点,动态移动机器人的时候计算。
-
预处理,将横纵坐标分别排序,二分查找个数更新。
-
用数据结构(动态开点线段树
树状数组)分别维护横纵坐标,计算个数。
复杂度
放一个法三的树状数组代码:
#include <bits/stdc++.h>
#define N 500005
#define int long long
const int inf = 0x7f7f7f7f;
using namespace std;
int n, m;
int lowbit(int x) {
return x & (-x);
}
struct BIT {
unordered_map<int, int>tree;
void point_add(int x, int val) {
while (x <= inf * 2) {
tree[x] += val;
x += lowbit(x);
}
}
int ask(int x) {
int ans_ask = 0;
while (x) {
ans_ask += tree[x];
x -= lowbit(x);
}
return ans_ask;
}
}X, Y;
int dis;
char s[300005];
signed main() {
scanf("%lld%lld", &n, &m);
for (int i = 1; i <= n; i++) {
int x, y;
scanf("%lld%lld", &x, &y);
dis += abs(x) + abs(y);
X.point_add(x + inf, 1);
Y.point_add(y + inf, 1);
}
scanf("%s", s);
int sx = inf, sy = inf;
for (int i = 0; i < m; i++) {
if (s[i] == 'I') {
int sl = X.ask(sx);
dis += sl;
dis -= n - sl;
++sx;
}
else if(s[i] == 'Z') {
int sr = X.ask(sx - 1);
dis -= sr;
dis += n - sr;
--sx;
}
else if(s[i] == 'S') {
int sd = Y.ask(sy);
dis += sd;
dis -= n - sd;
++sy;
}
else if(s[i] == 'J') {
int su = Y.ask(sy - 1);
dis -= su;
dis += n - su;
--sy;
}
cout << dis << "\n";
}
return 0;
}
极其不好想的题目。
从暴力做法开始考虑。暴力做法显然是暴力 乱搞
考虑优化空间是什么。由于字符串的个数是一定的
考虑每一次转移中变化了什么量。发现匹配到哪一个字符串会改变,匹配到模式串的位数也会改变。
因此考虑设
转移过程是如果是小写字母,就继续
代码的实现或许会有一些难度,详细见代码。
#include <bits/stdc++.h>
#define N 28
#define L 105
#define mod 10000
using namespace std;
int n;
char name;
char s[N][L];
int dp[N][L];
int pos[N][L];
int nxt[L];
char g[L], lg; //模式串
void get_nxt(char *t) {
lg = strlen(t + 1);
for (int i = 2, j = 0; i <= lg; i++) {
while (j && t[i] != t[j + 1])
j = nxt[j];
if (t[i] == t[j + 1])
j++;
nxt[i] = j;
}
}
int dfs(int num, int w) {
if (~dp[num][w]) //未经过
return dp[num][w];
// printf("%d %d\n", num, w);
dp[num][w] = 0;//归零
int l = strlen(s[num] + 1);
int p = w; //记录实时匹配位置
for (int t = 1; t <= l; t++) {
char ch = s[num][t];
if (ch >= 'a' && ch <= 'z') {
int j = p; //跳指针匹配
while (j && ch != g[j + 1])
j = nxt[j];
if (ch == g[j + 1])
j++;
if (j == lg) { //匹配成功
dp[num][w]++;
dp[num][w] %= mod;
}
p = j; //实时记录模式串匹配到哪一位
}
else {
dp[num][w] += dfs(ch - 'A' + 1, p);
dp[num][w] %= mod;
p = pos[ch - 'A' + 1][p];
}
}
pos[num][w] = p; //到最终再记录!
return dp[num][w];
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n >> name;
for (int i = 1; i <= n; i++) {
char ept;
cin >> ept >> ept;
cin >> (s[i] + 1);
}
memset(dp, -1, sizeof dp);
cin >> (g + 1);
get_nxt(g);
cout << dfs((int)(name - 'A' + 1), 0) << "\n";
return 0;
}
显然这个合法情况是不好算的。考虑到合法情况
具体的实现上,先预处理出
详见代码:
#include <bits/stdc++.h>
#define N 10005
using namespace std;
int n;
int a[N];
long long c[N];
int num[N];
long long ans;
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
num[a[i]] = 1;
}
for (int i = 1; i <= n; i++) {
for (int j = 2; j * j <= a[i]; j++) {
if (a[i] % j == 0) {
num[j]++;
if (j * j != a[i])
num[a[i] / j]++;
}
}
}
for (int i = 4; i <= 10000; i++)
c[i] = (long long)i * (i - 1) * (i - 2) * (i - 3) / 24;
ans = c[n];
for (int i = 2; i <= 10000; i++) {
int cnt = 0, x = i;
for (int j = 2; j * j <= i; j++)
if (x % j == 0) {
if ((x / j) % j == 0)
goto FS;
++cnt;
while (x % j == 0)
x /= j;
}
if (x > 1)
cnt++;
if (cnt % 2 == 1)
ans -= c[num[i]];
else
ans += c[num[i]];
FS:;
}
cout << ans << "\n";
return 0;
}
还是送分题,无论怎么乱搞预处理出素数,前缀和差分预处理一下可以做到
代码:
#include <bits/stdc++.h>
#define N 32780
using namespace std;
long long prm[N], tot;
bool vis[N];
void solve() {
for (int i = 2; i <= 32767; i++) {
if (!vis[i])
prm[++tot] = i;
for (int j = 1; j <= tot; j++)
if (i * prm[j] <= 32767)
vis[i * prm[j]] = true;
}
}
long long sum[N];
long long mp[N];
int main() {
solve();
for (int i = 1; i <= tot; i++)
sum[i] = sum[i - 1] + prm[i];
for (int i = 1; i <= tot; i++)
for (int j = i; j <= tot; j++)
if (sum[j] - sum[i - 1] <= 32767)
mp[sum[j] - sum[i - 1]]++;
int x;
while (scanf("%d", &x) && x)
printf("%d\n", mp[x]);
return 0;
}
基础 (我也不知道我是怎么挂的)
可以滚动数组压缩空间,但是小心会炸
代码:
// dp ?
// 变量:min,km,type,lazy
// min可以滚动数组成两维,type不用管
// 定义dp(i)[j][2]为yyc第 i 分钟后疲劳度为 j 时,当前状态是休息(0)/启动(1)时最多能走多少千米
#include <bits/stdc++.h>
#define D 1005
#define M 505
using namespace std;
int n, m;
int d[10005];
int dp[2][M][2];
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++)
scanf("%d", &d[i]);
int t = 1;
for (int i = 1; i <= n; i++, t ^= 1) {
for (int j = 0; j <= min(i, m); j++) {
if (j == 0)
dp[t][j][0] = max(dp[t ^ 1][j][0], max(dp[t ^ 1][j + 1][0], dp[t ^ 1][j + 1][1]));
else {
dp[t][j][0] = max(dp[t ^ 1][j + 1][0], dp[t ^ 1][j + 1][1]);
dp[t][j][1] = dp[t ^ 1][j - 1][1];
if (j == 1)
dp[t][j][1] = max(dp[t][j][1], dp[t ^ 1][0][0]);
dp[t][j][1] += d[i];
}
}
}
printf("%d\n", dp[t ^ 1][0][0]);
return 0;
}
容易发现对于每一列 证明?等式的性质。
于是我们可以只处理一个 考场上怎么就没想到 )
具体地,设
转移式子显然:
预处理!一切都要预处理! 的转移必须严格
代码:
#include <bits/stdc++.h>
#define int long long
#define N 10005
#define mod 1000000007
using namespace std;
int n, m, k;
int qpow(int x, int y) {
int ans = 1;
while (y) {
if (y & 1)
ans = ans * x % mod;
x = x * x % mod;
y >>= 1;
}
return ans;
}
int fac[N];
int ans;
int C(int n, int m) {
if (n < m || n < 0 || m < 0)
return 1;
return fac[n] * qpow(fac[n - m], mod - 2) % mod * qpow(fac[m], mod - 2) % mod;
}
int dp[105][10005];
// 定义dp[i][j]为第i列,已经放了j个点的方案数
int c[105] = {1};
int pw[105][2];
signed main() {
fac[0] = 1;
for (int i = 1; i < N; i++)
fac[i] = fac[i - 1] * i % mod;
cin >> n >> m >> k;
int t = m / n;
int w = m % n;
for (int i = 0; i <= n; i++) {
c[i] = C(n, i);
pw[i][0] = qpow(c[i], t);
pw[i][1] = qpow(c[i], t + 1);
}
dp[0][0] = 1;
for (int i = 1; i <= n; i++)
for (int j = 0; j <= k; j++)
for (int p = 0; p <= min(n, j); p++)
dp[i][j] = (dp[i][j] + (dp[i - 1][j - p] * ((i <= w) ? (pw[p][1]) : (pw[p][0])) % mod + mod) % mod) % mod;//,cout<<p<<" "<<c[p]<<" "<<C(n,p)<<"\n";
cout << dp[n][k] << "\n";
return 0;
}
思路其实并不难想。考虑一条边无法删去的情况是什么。假使一条边无法删去,它一定是
提醒:
-
数据不保证没有重边
-
数据不保证不卡
。
代码:
#include <bits/stdc++.h>
#define N 40005
#define M 800005
using namespace std;
int n, m;
struct Node {
int to, nxt, dis;
int pos;
}e[M];
int head[N], cnt;
void add(int u, int v, int w, int pos) {
e[++cnt].to = v;
e[cnt].pos = pos;
e[cnt].dis = w;
e[cnt].nxt = head[u];
head[u] = cnt;
}
struct tode {
int y, pos;
};
vector<tode>v[N];
bool vis[N];
struct node {
int x, dis;
bool operator < (const node &x) const{
return x.dis < dis;
}
};
priority_queue<node>q;
int dis[N];
void Dij(int s) {
dis[s] = 0;
q.push((node) {
s, 0
});
while (!q.empty()) {
node tmp = q.top();
q.pop();
int x = tmp.x;
if (vis[x])
continue;
vis[x] = true;
for (int i = head[x]; i; i = e[i].nxt) {
int y = e[i].to;
if (dis[y] >= dis[x] + e[i].dis) {
if (!vis[y])
q.push((node){
y, dis[x] + e[i].dis
});
if (dis[y] > dis[x] + e[i].dis)
v[y].clear();
v[y].push_back((tode) {
x, e[i].pos
});
dis[y] = dis[x] + e[i].dis;
}
}
}
}
vector<tode>V[N];
bool ins[N];
bool inc[M];
void redfs(int x) {
ins[x] = true;
for (int i = 0; i < (int)v[x].size(); i++) {
int y = v[x][i].y;
inc[v[x][i].pos] = true;
V[x].push_back((tode) {
y, v[x][i].pos
});
V[y].push_back((tode) {
x, v[x][i].pos
});
if (ins[y])
continue;
ins[y] = 1;
redfs(y);
}
}
int low[N], dfn[N];
int tim;
bool xycyx[M];
void Tarjan(int x, int fa) {
low[x] = dfn[x] = ++tim;
for (int i = 0; i < (int)V[x].size(); i++) {
int y = V[x][i].y;
if (!dfn[y]) {
Tarjan(y, x);
low[x] = min(low[x], low[y]);
if (low[y] > dfn[x])
xycyx[V[x][i].pos] = 1;
}
else if(dfn[y] < dfn[x] && y != fa)
low[x] = min(low[x], dfn[y]);
}
}
int s, t;
int main() {
scanf("%d%d%d%d", &n, &m, &s, &t);
for (int i = 1; i <= m; i++) {
int x, y, w;
scanf("%d%d%d", &x, &y, &w);
add(x, y, w, i);
add(y, x, w, i);
}
memset(dis, 0x7f, sizeof dis);
Dij(s);
redfs(t);
for (int i = 1; i <= n; i++)
if (!dfn[i])
Tarjan(i, 0);
int T;
cin >> T;
while (T--) {
int x;
scanf("%d", &x);
if (!inc[x])
puts("Yes");
else if (!xycyx[x])
puts("Yes");
else
puts("No");
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!