1028 模拟
2021/10/28 23:09:52
本文主要是介绍1028 模拟,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
t1 - 回家 home
小 Z 是个路痴。有一天小 Z 迷路了,此时小 Z 到家有 \(N\) 个单位长度。小 Z 可以进行若干次行动,每次行动小Z有 \(\frac{1}{2}\) 的概率向家靠近一个单位长度,有 \(\frac{1}{2}\) 的概率向家远离一个单位长度。由于小Z的体力是有限的,他最多能行动 \(K\) 次。请你帮帮可怜的小Z算一算,他在体力耗尽之前到达家中的概率是多少。
\(1\leq N,K\leq 5\cdot 10^6\)
考虑概率计算,用方案数/总情况.
考虑枚举在 \(i\) 步之后走到终点 .
那么,可以直接计算出向左走的步数和向右走的步数 . 看作是 \(+1\) 和 \(-1\) .
考虑每次向右走一步,纵坐标 \(+1\) 走了 \(n+1\) 步,\(-1\) 走了 \(m\) 步 .
那么从 \((0,0)\) 走到 \((n+m+1,n+1-m)\) 的方案数,其中横坐标不能走到 \(0\) 以下 .
首先忽略第二个性质,方案数为 \(\dbinom{n+m}{n}\) ,因为最后一步一定要为 \(+1\) ,所以方案数就是在剩下 \(n+m\) 步中选出 \(n\) 步向上 . 但是,其中必定存在一些向下的边,考虑一旦接触到了 \(-1\) ,就把后面的步数翻转,一一对应到一种新的走法,最后走到的位置还是 \((n+m+1,n+2-m)\) . 从上可知方案数为 \(\dbinom{n+m}{n+1}\) .
因此,方案数就是 \(\dbinom{n+m}{n}-\dbinom{n+m}{n+1}\) .
枚举 \(i\) 步之后走到终点 . \(+1\) 走了 \(n+\frac{i-n}{2}\) 步,\(-1\) 走了 \(\frac{i-n}{2}\) 次 .
那么,最后的答案即为
\[\sum\limits_{i=n}^{k}[(i-n)\%2=0]\frac{\binom{i-1}{n+\frac{i-n}{2}-1}-\binom{i-1}{n+\frac{i-n}{2}}}{2^i} \]只需要 \(O(n)\) 地预处理出阶乘,阶乘的逆元即可 .
时间复杂度 : \(O(n)\)
空间复杂度 : \(O(n)\)
code
#include<bits/stdc++.h> using namespace std; const int mod=998244353; int n,k; int fact[5000010],inv[5000010],inv2[5000010]; inline int ksm(int x,int k){ if(k==0)return 1; int res=ksm(x,k>>1); res=1ll*res*res%mod; if(k&1)res=1ll*res*x%mod; return res; } inline int C(int a,int b){ return 1ll*fact[a]*inv[b]%mod*inv[a-b]%mod; } int main(){ freopen("home.in","r",stdin); freopen("home.out","w",stdout); ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); cin>>n>>k; fact[0]=1; for(int i=1;i<=k;i++)fact[i]=1ll*i*fact[i-1]%mod; inv[k]=ksm(fact[k],mod-2); for(int i=k-1;i>=0;i--)inv[i]=1ll*inv[i+1]*(i+1)%mod; inv2[0]=1;inv2[1]=ksm(2,mod-2); for(int i=2;i<=k;i++)inv2[i]=1ll*inv2[i-1]*inv2[1]%mod; int ans=0; for(int i=n;i<=k;i+=2){ int tmp=1ll*(C(i-1,n+(i-n)/2-1)-C(i-1,n+(i-n)/2)+mod)%mod*inv2[i]%mod; ans=(ans+tmp)%mod; } cout<<ans<<endl; return 0; } /*inline? ll or int? size? min max?*/
题目来源 : nflsoj 20034 #12514. 「NOIP2021模拟赛石室中学2」回家
t2 - 最小化 min
小 F 有一个长度为 \(n\) 的正整数序列 \(A\) 。他的好朋友小 T 提出了这样的一个概念,对于一个序列 \(A\) ,它的 \(K\) 项差和为所有相邻 \(K\) 项的元素的差的绝对值的和。以序列 \(A\) 为例,它的 \(K\) 项差和为:
\[\sum\limits_{i=1}^{n-k}|A_i-A_{i+k}| \] 现在小 F 想求序列 \(A\) 的 \(k\) 项差和,为了使序列 \(A\) 的 \(k\) 项差和尽可能地小,小 F 决定对序列 \(A\) 进行重新排序。
现在,请你帮助小 F 求出对序列 \(A\) 进行任意重排后其 \(K\) 项差和的最小值。
\(1\leq n\leq 10^6,1\leq k\leq 5000,1\leq A_i\leq 10^9\)
对于 \(A_i\) ,只有 \(|A_i-A_{i-k}|\) 和 \(|A_i-A_{i+k}|\) 可能会对答案产生贡献 .
所以,对于 \(A_i\) 和 \(A_j\) ,如果 \(i\mod k\not= j\mod k\) 两个值不会互相影响 .
所以,把 \(i\mod k\) 相同的数都放在一起 .
考虑对于下标 \(1,1+k,1+2k,\cdots,1+ak\) 的值,怎样安排才能使产生的代价最小呢?
猜是 sort 一遍,从小到达填入 \(1,1+k,1+2k,\cdots ,1+ak\) 中 ,产生的代价是 \(|A_{1+ak}-A_{1}|\) .
考虑对于模 \(k\) 余 \(0,1,2,\cdots ,k-1\) 序列怎么分配 .
首先,把 \(A\) 从小到大排序,对于模 \(k\) 相同的序列选择的必须是连续的一段 .
对于模 \(k\) 余 \(0\) 到 \(k-1\) 的序列长度只有两种情况 ,\(l_1=\lfloor \frac{n}{k}\rfloor\) 和 \(l_2=\lfloor \frac{n}{k}\rfloor +1\) ,个数分别为 $s_1=n-k\lfloor \frac{n}{k}\rfloor $ 和 $s_2=k-n+k\lfloor \frac{n}{k}\rfloor $
此时可以考虑 \(dp\) .
用 \(f(i,j)\) 表示第一种长度的序列选了 \(i\) 个,第二种长度的序列选了 \(j\) 个的最小代价.
转移 \(dp\) 转移方程 .
\(f(i,j)\longrightarrow f(i+1,j)+|A_{(i+1)l_1+jl_2}-A_{il_1+jl_2+1}|\)
\(f(i,j)\longrightarrow f(i,j+1)+|A_{il_1+(j+1)l_2}-A_{il_1+jl_2+1}|\)
最小贡献即为 \(f(s_1,s_2)\) .
时间复杂度 : \(O(n\log n+m^2)\)
空间复杂度 : \(O(n+m)\)
code
#include<bits/stdc++.h> using namespace std; inline int read(){ char ch=getchar(); while(ch<'0'||ch>'9')ch=getchar(); int res=0; while(ch>='0'&&ch<='9'){ res=(res<<3)+(res<<1)+ch-'0'; ch=getchar(); } return res; } inline void print(int res){ if(res==0){ putchar('0'); return; } int a[10],len=0; while(res>0){ a[len++]=res%10; res/=10; } for(int i=len-1;i>=0;i--) putchar(a[i]+'0'); } const int inf=1e9+10; int n,k; int a[1000010]; int f[5010][5010]; inline void upd(int &x,int y){x=min(x,y);} int main(){ freopen("min.in","r",stdin); freopen("min.out","w",stdout); n=read();k=read(); for(int i=0;i<n;i++)a[i]=read(); sort(a,a+n); for(int i=0;i<5010;i++)for(int j=0;j<5010;j++)f[i][j]=inf; int s1=n-n/k*k,s2=k-s1; f[0][0]=0; for(int i=0;i<=s1;i++)for(int j=0;j<=s2;j++){ if(f[i][j]==inf)continue; upd(f[i+1][j],f[i][j]+a[(i+1)*(n/k+1)+j*(n/k)-1]-a[i*(n/k+1)+j*(n/k)]); upd(f[i][j+1],f[i][j]+a[i*(n/k+1)+(j+1)*(n/k)-1]-a[i*(n/k+1)+j*(n/k)]); } print(f[s1][s2]); return 0; } /*inline? ll or int? size? min max?*/ /* 6 3 4 3 4 3 2 5 */
题目来源 : nflsoj 20034 #12515. 「NOIP2021模拟赛石室中学2」最小化
t3 - 集合 set
小 W 有一个可重正整数集合 \(S\)。集合 \(S\) 起初为空集。小 W 想对集合进行以下操作。
1.向集合中插入一个正整数 \(a\) 。
2.每次令集合中 \(c\) 个正整数减小 \(1\),求最多进行的操作次数(该操作不改变集合中元素本身的值)。
现在,请您编写一个程序,帮助小W实现上述功能。
\(1\leq n\leq 10^6\)
时限 \(1s\) . 应该是道卡场题 .
正着考虑最多操作次数很难 . 考虑倒着想 . 因为可进行的最多操作次数具有单调性,考虑二分 .
二分当前最多操作次数是 \(mid\) . 那么,用到的个数是 \(c\times mid\) .
考虑 \(S\) 中的比 \(mid\) 小的数,必定可以被全部用掉,贡献是 \(\sum a_i\) 次 .
考虑比 \(mid\) 大的数 ,最多只会被用 \(mid\) 次 , 有 \(x\) 个,贡献就是 \(x\times mid\) .
\(x\times mid+\sum a_i\geq c\times mid\) .
需要维护 \(\sum a_i\) 和 \(x\) ,这两个东西都可以用 bit 来维护 .
但是加上二分 ,时间复杂度就是双 log ,要 t 飞了 .
此时考虑在 bit 上二分. 就解决了这个问题 .
时间复杂度 : \(O(n\log n)\)
空间复杂度 : \(O(n)\)
code
#include<bits/stdc++.h> using namespace std; inline int read(){ char ch=getchar(); while(ch<'0'||ch>'9')ch=getchar(); int res=0; while(ch>='0'&&ch<='9'){ res=(res<<3)+(res<<1)+ch-'0'; ch=getchar(); } return res; } inline void print(long long res){ if(res==0){ putchar('0'); return; } int a[20],len=0; while(res>0){ a[len++]=res%10; res/=10; } for(int i=len-1;i>=0;i--) putchar(a[i]+'0'); } int q,n=0; int type[1000010],a[1000010]; int b[2000010],m=0; int bit1[2000010]; long long bit2[2000010]; inline void upd(int i){ int val=b[i]; while(i<=m){ bit1[i]++; bit2[i]+=val; i+=i&-i; } } inline int sum1(int i){ int res=0; while(i){ res+=bit1[i]; i-=i&-i; } return res; } inline long long sum2(int i){ long long res=0; while(i){ res+=bit2[i]; i-=i&-i; } return res; } inline long long qry(int c){ if(c>n)return 0; int i=0; int sum1=0; long long sum2=0; for(int k=19;k>=0;k--){ if(i+(1<<k)>m)continue; if((sum2+bit2[i+(1<<k)])>=1ll*(c-(n-sum1-bit1[i+(1<<k)]))*b[i+(1<<k)]){ sum2+=bit2[i+(1<<k)]; sum1+=bit1[i+(1<<k)]; i+=1<<k; } } return 1ll*sum2/(c-(n-sum1)); } int main(){ freopen("set.in","r",stdin); freopen("set.out","w",stdout); q=read(); for(int i=0;i<q;i++){ type[i]=read(); a[i]=read(); if(type[i]==1)b[++m]=a[i]; } sort(b+1,b+m+1); m=unique(b+1,b+m+1)-b-1; for(int i=0;i<q;i++){ if(type[i]==1){ int id=lower_bound(b+1,b+m+1,a[i])-b; n++; upd(id); }else{ print(qry(a[i])); putchar('\n'); } } return 0; } /*inline? ll or int? size? min max?*/ /* 6 1 5 1 7 2 2 1 1 2 2 2 1 */
题目来源 : nflsoj 20034 #12517. 「NOIP2021模拟赛石室中学2」王国
这篇关于1028 模拟的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-06-26结对编程到底难不难?答案在这里
- 2024-06-19《2023版Java工程师》课程升级公告
- 2024-06-15matplotlib作图不显示3D图,怎么办?
- 2024-06-1503-Loki 日志监控
- 2024-06-1504-让LLM理解知识 -Prompt
- 2024-06-05做软件测试需要懂代码吗?
- 2024-06-0514-ShardingSphere的分布式主键实现
- 2024-06-03为什么以及如何要进行架构设计权衡?
- 2024-05-31全网首发第二弹!软考2024年5月《软件设计师》真题+解析+答案!(11-20题)
- 2024-05-31全网首发!软考2024年5月《软件设计师》真题+解析+答案!(21-30题)