2023/7/8 模拟赛
2023/7/8 模拟赛 赛后总结
赛时 T4 蛮可惜的,推的其实差不多了,就是偏了一点点…… T2 忘了骗分了,有点可惜。
T1 礼物
题目描述
小T要给他的妹妹买礼物。他会不断的买,直到自己身上没有足够的钱来买任何一件礼物为止,他想知道有多少种方案符合他买礼物的方式。
我们认为两种选择方案不同当且仅当它们选取的的物品的集合不同。
因为答案可能很大,你只需要输出答案对
思路
首先这道题有一点很重要:必须买到买不了其他物品为止。我们就从这里入手。我们考虑,如果买东西买到买不了其他物品,当且仅当当前未购买的物品中,价值最小的物品的花费要大于剩余钱数。我们直接去做背包显然不现实,因为我们无法确定是否到达最后的状态;这时,我们发现第二个性质,就是如果无法继续购买,那么价值小于(或者某些等于)当前未购买的价值最小的物品的所有物品都应该已经被买走。
这就启发我们,将物品按价值从小到大排序,然后让我们去从小到大枚举这个最后买不了的东西,这样,它之前的所有的物品都应该已经被买走,而它本身未被买走只能说明是后面的物品购买造成了影响。这时,我们对后面的物品做背包就好。我们令
但是,如果每次都做一遍背包,这样总复杂度就是 虽然随机数据有人水过去了。我们考虑优化。机房另一位大佬提供了一个思路,就是从后往前枚举,这样可以不用每次重新做背包,只需要去不断更新就好。我赛场上反正没想到,但是有了一种也不失优秀,并且比较巧妙的做法。
我们发现,我们每次都清空
代码:
点击查看代码
#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国有
参考洛谷 P3258 松鼠的新家,树链剖分+树上差分随便搞搞就行。实测
代码:
点击查看代码
#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 计算
题面描述
给定
合法的
思路
这个题可能是最有意思的一道题。首先我们来推式子。
我们设
我们令
由我们上面推得的性质,对于每一个
我们又发现,对于每个
代码:
点击查看代码
#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),两道计数,考得还算满意吧,虽然也有些地方有遗憾就是了。