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」王国
标签:10,frac,int,res,1028,leq,模拟,mod 来源: https://www.cnblogs.com/suadwm/p/15478105.html