2023/7/8 模拟赛

2023/7/8 模拟赛 赛后总结

赛时 T4 蛮可惜的,推的其实差不多了,就是偏了一点点…… T2 忘了骗分了,有点可惜。

T1 礼物

题目描述

小T要给他的妹妹买礼物。他会不断的买,直到自己身上没有足够的钱来买任何一件礼物为止,他想知道有多少种方案符合他买礼物的方式。

我们认为两种选择方案不同当且仅当它们选取的的物品的集合不同。

因为答案可能很大,你只需要输出答案对107+7取模的结果即可。

思路

首先这道题有一点很重要:必须买到买不了其他物品为止。我们就从这里入手。我们考虑,如果买东西买到买不了其他物品,当且仅当当前未购买的物品中,价值最小的物品的花费要大于剩余钱数。我们直接去做背包显然不现实,因为我们无法确定是否到达最后的状态;这时,我们发现第二个性质,就是如果无法继续购买,那么价值小于(或者某些等于)当前未购买的价值最小的物品的所有物品都应该已经被买走。
这就启发我们,将物品按价值从小到大排序,然后让我们去从小到大枚举这个最后买不了的东西,这样,它之前的所有的物品都应该已经被买走,而它本身未被买走只能说明是后面的物品购买造成了影响。这时,我们对后面的物品做背包就好。我们令 fi 表示买 i 元钱的东西的方案数,枚举后面的每个价值 cj,有 fi=ficj。最后,我们需要计入答案的就是剩余钱数小于当前枚举物品价值的 fi
但是,如果每次都做一遍背包,这样总复杂度就是 n2m虽然随机数据有人水过去了。我们考虑优化。机房另一位大佬提供了一个思路,就是从后往前枚举,这样可以不用每次重新做背包,只需要去不断更新就好。我赛场上反正没想到,但是有了一种也不失优秀,并且比较巧妙的做法。
我们发现,我们每次都清空 f 比较浪费,那有没有办法只去除没有用的贡献呢?我们考虑 dp 过程:每次,我们都是通过前面的某个 fi 值转移到 fj 这里,那是不是,我们每次把 fj 减走原来 fi 造成的贡献就行了?又因为我们是从左到右枚举进行的 dp,那么我们再从左到右枚举,每次遇到一个非零的值,就把它造成的转移贡献减走就可以了。这样做后,复杂度变成了优秀的 nm
代码:

点击查看代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1050, mod = 10000007;
inline int read(){
int x = 0, f = 1; char ch = getchar();
while(ch<'0' || ch>'9'){if(ch == '-') f = -1; ch = getchar();}
while(ch>='0'&&ch<='9'){x = x*10+ch-48, ch = getchar();}
return x * f;
}
int n, m;
int f[N];
int a[N];
ll sum;
int ans;
int main(){
n = read(), m = read();
for(int i = 1; i<=n; ++i){
a[i] = read();
sum+=a[i];
}
if(sum<=m){//特判一,没啥好说的(
puts("1");
return 0;
}
sort(a+1, a+n+1);
int st = 1;
while(a[st] == 0){//特判二,注意去掉开头的 0(否则会有 bug)
++st;
}
f[0] = 1;
for(int i = st; i<=n; ++i){
for(int j = m; j>=a[i]; --j){
f[j] = (1ll*f[j]+f[j-a[i]])%mod;
}
}
int lst = m;
for(int i = st; i<=n; ++i){
for(int j = 0; j<=m; ++j){//去除贡献
if(f[j] && j+a[i]<=m){
f[j+a[i]] = ((f[j+a[i]]-f[j])%mod+mod)%mod;
}
}
for(int j = m; j>max(m-a[i], -1); --j){//统计答案
ans = (1ll*ans+f[j])%mod;
}
m-=a[i];
if(m<0) break;
}
ans = (ans%mod+mod)%mod;
printf("%lld\n", ans);
return 0;
}

T2 课程

考场上没想出来……有人写状压+骗分拿了 70 pts,正解是随机化。
还没改出来,咕咕咕~

T3 火车

题面描述

A国有 n 个城市,城市之间有一些双向道路相连,并且城市两两之间有唯一路径。现在有火车在城市 a ,需要经过 m 个城市。火车按照以下规则行驶:每次行驶到还没有经过的城市中在 m 个城市中最靠前的。现在想知道火车经过这 m 个城市后所经过的道路数量。

参考洛谷 P3258 松鼠的新家,树链剖分+树上差分随便搞搞就行。实测 nlog2n 拿线段树整可过。
代码:

点击查看代码
#include<bits/stdc++.h>
#define ls tr<<1
#define rs tr<<1 | 1
#define ll long long
using namespace std;
const int N = 500050, M = 400040;
inline int read(){
int x = 0, f = 1; char ch = getchar();
while(ch<'0' || ch>'9'){if(ch == '-') f = -1; ch = getchar();}
while(ch>='0'&&ch<='9'){x = x*10+ch-48, ch = getchar();}
return x * f;
}
int head[N], tot;
struct node{
int nxt, to;
}edge[N<<1];
void add(int u, int v){
edge[++tot].nxt = head[u];
edge[tot].to = v;
head[u] = tot;
}
int dep[N], siz[N], dfn[N], top[N], totd, fa[N], son[N];
void dfs1(int u, int fath){
siz[u] = 1;
fa[u] = fath;
dep[u] = dep[fath]+1;
for(int i = head[u]; i; i = edge[i].nxt){
int v = edge[i].to;
if(v == fath) continue;
dfs1(v, u);
siz[u]+=siz[v];
if(siz[son[u]]<siz[v]) son[u] = v;
}
}
void dfs2(int u, int Top){
top[u] = Top;
dfn[u] = ++totd;
if(!son[u]) return;
dfs2(son[u], Top);
for(int i = head[u];i ; i = edge[i].nxt){
int v = edge[i].to;
if(!dfn[v]) dfs2(v, v);
}
}
bool tree[N<<2], lazy[N<<2];
void push_down(int tr){
if(lazy[tr]){
tree[ls] = 1;
tree[rs] = 1;
lazy[ls] = lazy[rs] = 1;
lazy[tr] = 0;
}
}
void modify(int tr, int L, int R, int lq, int rq, int val){
if(lq<=L && R<=rq){
tree[tr] = val;
lazy[tr] = val;
return;
}
push_down(tr);
int mid = (L+R)>>1;
if(lq<=mid) modify(ls, L, mid, lq, rq, val);
if(mid<rq) modify(rs, mid+1, R, lq, rq, val);
}
bool query(int tr, int L, int R, int pos){
if(L == R){
return tree[tr];
}
push_down(tr);
int mid = (L+R)>>1;
if(pos <= mid) return query(ls, L, mid, pos);
else return query(rs, mid+1, R, pos);
}
inline int LCA(int x, int y){
while(top[x] != top[y]){
if(dep[top[x]] < dep[top[y]]) swap(x, y);
modify(1, 1, totd, dfn[top[x]], dfn[x], 1);
x = fa[top[x]];
}
if(dep[x]>dep[y]) swap(x, y);
modify(1, 1, totd, dfn[x], dfn[y], 1);
return x;
}
int n, m, st;
//int city[M];
long long w[N];
ll ans = 0;
void dfs3(int u, int fath){
for(int i = head[u]; i; i = edge[i].nxt){
int v = edge[i].to;
if(v == fath) continue;
dfs3(v, u);
w[u]+=w[v];
}
ans+=w[u];
}
int main(){
n = read(), m = read(), st = read();
for(int i = 1;i<n; ++i){
int u = read(), v = read();
add(u, v);
add(v, u);
}
dfs1(1, 0);
dfs2(1, 1);
int u = st;
for(int i = 1; i<=m; ++i){
int v = read();
if(query(1, 1, totd, dfn[v])) continue;
int lca = LCA(u, v);
w[u]+=1;
w[v]+=1;
w[lca]-=2;
u = v;
}
dfs3(1, 0);
printf("%lld\n", ans);
return 0;
}

T4 计算

题面描述

给定 n,求合法的 (x1,x2,x3,...,x2m) 组数。一组 x 是合法的,当且仅当

i[1,2m],xiZ+,xi|n

i=12mxinm

合法的 (x1,x2,x3,,x2m) 可能很多,请你输出方案数 mod 998244353

思路

这个题可能是最有意思的一道题。首先我们来推式子。
我们设 F(x)=i=12mxiG(x)=i=12mnxi,显然 F(x)G(x)=n2m,而 F(x)n2m,移项得 G(x)n2m
我们令 S1 表示 F(x)<n2m 的方案数,S2 表示 F(x)=n2m 的方案数,S3 表示 F(x)>n2m 的方案数。
由我们上面推得的性质,对于每一个 S1 中的方案,必然对应着一个 S3 中的方案,也就是说,S1=S3,这样,我们只需要处理 S2 就行了。
我们又发现,对于每个 n 的质因数都可以分开考虑,也就是,我们去考虑怎么把质因数去填到 这 2mxi 上。我们设 fi,j 表示当前填到第 ix,填了 j 个质因数 p,有转移 fi,j=k=0kmfi1,jk
代码:

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N = 6600, mod = 998244353;
int f[205][N];
int n, m;
int don = 1;
int s2 = 1;
void solve(int x, int num){
memset(f, 0, sizeof(f));
f[0][0] = 1;
for(int i = 1; i<=m*2; ++i){
for(int j = 0; j<=num*m; ++j){
for(int k = 0; k<=min(num, j); ++k){
f[i][j] = (f[i][j]+f[i-1][j-k])%mod;
}
}
}
don = (1ll*don*(num+1))%mod;
s2 = (1ll*s2*f[m*2][num*m])%mod;
}
inline int fpow(int a, int b){
a%=mod;
int ret = 1;
while(b){
if(b & 1){
ret = (1ll*ret*a)%mod;
}
b>>= 1;
a = (1ll*a*a)%mod;
}
return ret;
}
signed main(){
scanf("%d%d", &n, &m);
int cnt;
int nn = n;
for(int i = 2; i*i<=nn; ++i){
if(n%i == 0){
cnt = 0;
while(n%i == 0){
n/=i;
cnt++;
}
solve(i, cnt);
}
}
if(n>1){
solve(n, 1);
}
int ans = fpow(don, 2*m);
ans = (1ll*ans+s2)%mod;
ans = ((ans%2)?(ans+mod)/2:ans/2);//注意这里会除以二,需要特判。
printf("%d\n", ans%mod);
return 0;
}

总结:这次一道 ds,一道乱搞(bush),两道计数,考得还算满意吧,虽然也有些地方有遗憾就是了。

posted @   霜木_Atomic  阅读(30)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示