13.【初三奥赛模拟测试2】
- 估计也打不了多少 \(qwq\)
\(\Huge终于不垫底了。qwq\)
初三奥赛模拟测试2
T1南
题解
- 一道概率期望。
- 一般都是从 \(n\) 开始递推到 \(0\) 。
- 假设我们现在有 \(i\) 种枪,那么期望次数
\[\large f_i=f_{i+1}+\frac n{n-i}
\]
- 因为当前有 \(i\) 种可能买到已经买过的枪, \(n-i\) 种可能买到未买过的枪。直接加上即可。
- 期望的价钱就是
\[\large g_i=g_{i+1}+\frac n{n-i}\times f_{i}
\]
- 假如每把枪的价格都是 \(1\) ,显然结果就是期望次数。
- 由于每次购买价格需要 \(+1\) , 因此也很容易就能得出买到下一把枪的期望价钱就是买到这把枪的期望次数,直接乘起来即可。
代码
无优化
#include<bits/stdc++.h>
#define int long long
#define N (1000010)
#define I i
#define J j
#define raed read
#define reaD read
#define reAD read
#define rEAD read
#define READ read
#define REAd read
#define REad read
#define Read read
#define Reda read
#define redA read
#define reDA read
#define redA read
#define itn signed
#define Itn signed
#define ITN signed
#define Int signed
#define INT signed
#define foR for
#define fot for
#define foT for
#define sort stable_sort
using namespace std;
namespace IO
{
#define ll long long
const int MAX=1<<6;
char buf[MAX],*p1=buf,*p2=buf;
char obuf[MAX],*o=obuf;
#define gc()(p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<6,stdin),p1==p2)?EOF:*p1++)
//template<typename T>
//inline T read()
inline int read()
{
int x=0;bool f=1;
char c=gc();
for(;c<48||c>57;c=gc())if(c=='-')f=0;
for(;c>=48&&c<=57;c=gc())x=(x<<3)+(x<<1)+(c^48);
return f?x:~x+1;
}
void print(ll x){if(x>9)print(x/10);*o++=(x%10)+'0';}
void pit(ll x){if(x<0)*o++='-',x=~x+1;print(x);}
void write(ll x,char ed){pit(x);*o++=ed;}
void flush(){fwrite(obuf,o-obuf,1,stdout);}
#undef ll
}
using IO::read;using IO::write;using IO::flush;using std::complex;
inline int min(int x,int y){return y&((y-x)>>31)|x&(~(y-x)>>31);}
inline int max(int x,int y){return x&((y-x)>>31)|y&(~(y-x)>>31);}
inline void swap(int &x,int &y){x^=y^=x^=y;}
long long n,m;
void init_set()
{
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
}
int tot,t,x,y;
int furina,fucaros;
double panzer[10010],nahida[10010];
signed main()
{
init_set();
n=read();
for(int i(n-1);i>=0;--i)
{
nahida[i]=1.0*nahida[i+1]+1.0*n/(n-i);
panzer[i]=1.0*panzer[i+1]+nahida[i]*1.0*n/(n-i);
}
printf("%.2f\n",panzer[0]);
flush();
return 0;
}
- 由于递推时只需要当前以及上一个状态,因此用四个变量即可替代。
有点优化
#include<bits/stdc++.h>
#define int long long
#define N (1000010)
#define I i
#define J j
#define raed read
#define reaD read
#define reAD read
#define rEAD read
#define READ read
#define REAd read
#define REad read
#define Read read
#define Reda read
#define redA read
#define reDA read
#define redA read
#define itn signed
#define Itn signed
#define ITN signed
#define Int signed
#define INT signed
#define foR for
#define fot for
#define foT for
#define sort stable_sort
using namespace std;
namespace IO
{
#define ll long long
const int MAX=1<<6;
char buf[MAX],*p1=buf,*p2=buf;
char obuf[MAX],*o=obuf;
#define gc()(p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<6,stdin),p1==p2)?EOF:*p1++)
//template<typename T>
//inline T read()
inline int read()
{
int x=0;bool f=1;
char c=gc();
for(;c<48||c>57;c=gc())if(c=='-')f=0;
for(;c>=48&&c<=57;c=gc())x=(x<<3)+(x<<1)+(c^48);
return f?x:~x+1;
}
void print(ll x){if(x>9)print(x/10);*o++=(x%10)+'0';}
void pit(ll x){if(x<0)*o++='-',x=~x+1;print(x);}
void write(ll x,char ed){pit(x);*o++=ed;}
void flush(){fwrite(obuf,o-obuf,1,stdout);}
#undef ll
}
using IO::read;using IO::write;using IO::flush;using std::complex;
inline int min(int x,int y){return y&((y-x)>>31)|x&(~(y-x)>>31);}
inline int max(int x,int y){return x&((y-x)>>31)|y&(~(y-x)>>31);}
inline void swap(int &x,int &y){int tmp=x;x=y;y=tmp;}
long long n,m;
void init_set()
{
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
}
int tot,t,x,y;
int furina,fucaros;
double panzer,sumeru,nahida,fontaine;
signed main()
{
init_set();
n=read();
for(int i(n-1);i>=0;--i)
nahida=fontaine+1.0*n/(n-i),
panzer=1.0*sumeru+nahida*n/(n-i),
fontaine=nahida,sumeru=panzer;
printf("%.2f\n",panzer);
flush();
return 0;
}
- 最后发现竟然能优化成这样!
for(int i(n-1);i>=0;--i)
panzer+=(nahida+=1.0*n/(n-i))*n/(n-i);
printf("%.2f\n",panzer);
- 直接用两个变量不断更新就可以了 \(qwq\) 。
- 然而这时候 \(for\) 循环顺序也不重要了。(其实打的还是一个东西)
for(int i(1);i<=n;++i)
panzer+=(nahida+=1.0*n/i)*n/i;
printf("%.2f\n",panzer);
T4义
题解
- 是个背包。
- 显然, \(\leq\sqrt n\) 的物品都有拿完的可能。
- 显然, \(>\sqrt n\) 的物品没有可能拿完。
- 所以分开 \(dp\) 。
- 设 \(\large f_{i,j}\) 为取到 \(i(i\leq\sqrt n)\) 背包容量为 \(j\) 的方案数。
- 当然可以不拿 \(\large f_{i-1,j}\) 或者拿一个 \(\large f_{i,j-i}\) ,但是拿一个之后就要考虑是否多拿了。因为第 \(i\) 个物品只有 \(i\) 个,因此要减去 \(\large f_{i-1,j-i\times(i+1)}\) ,这样就保证了不会多拿物品,同时在没有拿到 \(i\) 个物品时也不会产生影响。所以柿子就是
\[\Large f_{i,j}=f_{i-1,j}+f_{i,j-i}+f_{i-1,j-i\times(i+1)}
\]
- 回来考虑 \(i>\sqrt n\) 的部分。
- 设 \(\large g_{i,j}\) 为拿了 \(i\) 个物品时,背包容量为 \(j\) 的方案数。
- 有两个操作,可能有些抽象。
- 第一个是拿一个 \(\sqrt n+1\) 物品
\[\Large g_{i+1,j+\sqrt n+1}+=g_{i,j}
\]
- 第二个是将现有物品都变成下一个物品,即都增加 \(1\) 的容量
\[\Large g_{i,j+i}+=g_{i,j}
\]
- 虽然自始至终没有添加过 \(>\sqrt n+1\) 的物品,但是通过第二个操作则可以转化出 \(>\sqrt n+1\) 的物品。
- 之后进行合并,直接把对应的容量相加 为 \(n\) 的乘上再累加即可。
代码
代码
#include<bits/stdc++.h>
// #define int long long
#define N (1000010)
#define I i
#define J j
#define raed read
#define reaD read
#define reAD read
#define rEAD read
#define READ read
#define REAd read
#define REad read
#define Read read
#define Reda read
#define redA read
#define reDA read
#define redA read
#define itn signed
#define Itn signed
#define ITN signed
#define Int signed
#define INT signed
#define foR for
#define fot for
#define foT for
#define sort stable_sort
using namespace std;
namespace IO
{
#define ll long long
const int MAX=1<<6;
char buf[MAX],*p1=buf,*p2=buf;
char obuf[MAX],*o=obuf;
#define gc()(p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<6,stdin),p1==p2)?EOF:*p1++)
//template<typename T>
//inline T read()
inline int read()
{
int x=0;bool f=1;
char c=gc();
for(;c<48||c>57;c=gc())if(c=='-')f=0;
for(;c>=48&&c<=57;c=gc())x=(x<<3)+(x<<1)+(c^48);
return f?x:~x+1;
}
void print(ll x){if(x>9)print(x/10);*o++=(x%10)+'0';}
void pit(ll x){if(x<0)*o++='-',x=~x+1;print(x);}
void write(ll x,char ed){pit(x);*o++=ed;}
void flush(){fwrite(obuf,o-obuf,1,stdout);}
#undef ll
}
using IO::read;using IO::write;using IO::flush;using std::complex;
inline int min(int x,int y){return y&((y-x)>>31)|x&(~(y-x)>>31);}
inline int max(int x,int y){return x&((y-x)>>31)|y&(~(y-x)>>31);}
inline void swap(int &x,int &y){x^=y^=x^=y;}
long long n,m;
void init_set()
{
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
}
int tot,t,x,y,P(23333333);
int furina,fucaros,fontaine,nahida;
int panzer[2][100010],bomber[343][100010];
inline void cal(int&x){if(x>=P)x-=P;}
signed main()
{
init_set();
n=read();m=sqrt(n);
panzer[0][0]=panzer[1][0]=1;
for(int i(1);i<=m;++i)
for(int j(1);j<=n;++j)
{
panzer[i&1][j]=panzer[!(i&1)][j];
if(j>=i)
panzer[i&1][j]+=panzer[i&1][j-i],
cal(panzer[i&1][j]);
if(j>=i*(i+1))
panzer[i&1][j]=panzer[i&1][j]-panzer[!(i&1)][j-i*(i+1)]+P,
cal(panzer[i&1][j]);
}
bomber[0][0]=1;
for(int i(0);i<=m;++i)
for(int j(0);j<=n;++j)
{
if(j+i<=n&&i)
bomber[i][j+i]+=bomber[i][j],
cal(bomber[i][j+i]);
if(j+m+1<=n)
bomber[i+1][j+m+1]+=bomber[i][j],
cal(bomber[i+1][j+m+1]);
}
bomber[1][0]=1;
for(int i(0);i<=n;++i)
for(int j(1);j<=m;++j)
nahida=nahida+(1ll*panzer[m&1][i]*bomber[j][n-i])%P,
cal(nahida);
write(nahida,'\n');
flush();
return 0;
}
- 然鹅在 \(loj\) 上看到了这个鬼畜优化
优化
#include<bits/stdc++.h>
// #define int long long
#define N (1000010)
#define I i
#define J j
#define raed read
#define reaD read
#define reAD read
#define rEAD read
#define READ read
#define REAd read
#define REad read
#define Read read
#define Reda read
#define redA read
#define reDA read
#define redA read
#define itn signed
#define Itn signed
#define ITN signed
#define Int signed
#define INT signed
#define foR for
#define fot for
#define foT for
#define sort stable_sort
using namespace std;
namespace IO
{
#define ll long long
const int MAX=1<<6;
char buf[MAX],*p1=buf,*p2=buf;
char obuf[MAX],*o=obuf;
#define gc()(p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<6,stdin),p1==p2)?EOF:*p1++)
//template<typename T>
//inline T read()
inline int read()
{
int x(0);bool f(true);
char c(gc());
for(;c<48||c>57;c=gc())if(c=='-')f=false;
for(;c>=48&&c<=57;c=gc())x=(x<<3)+(x<<1)+(c^48);
return f?x:~x+1;
}
void print(ll x){if(x>9)print(x/10);*o++=(x%10)+'0';}
void pit(ll x){if(x<0)*o++='-',x=~x+1;print(x);}
void write(ll x,char ed){pit(x);*o++=ed;}
void flush(){fwrite(obuf,o-obuf,1,stdout);}
#undef ll
}
using IO::read;using IO::write;using IO::flush;using std::complex;
inline int min(int x,int y){return y&((y-x)>>31)|x&(~(y-x)>>31);}
inline int max(int x,int y){return x&((y-x)>>31)|y&(~(y-x)>>31);}
inline void swap(int &x,int &y){x^=y^=x^=y;}
int n,m;
void init_set()
{
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
}
int tot,t,x,y,P(23333333);
int furina,fucaros,fontaine,nahida;
int panzer[100010],bomber[100010];
int battleship[100010];
inline void cal(int&x){if(x>=P)x-=P;}
signed main()
{
init_set();
n=read();m=sqrt(n);
panzer[0]=1;
for(int i(1);i<=m;++i)
{
for(int j(i);j<=n;++j)
panzer[j]+=panzer[j-i],
cal(panzer[j]);
for(int j(n);j>=i*(i+1);--j)
panzer[j]-=panzer[j-i*(i+1)]-P,
cal(panzer[j]);
}
bomber[0]=1;battleship[0]=1;
for(int i(1);i<=m;++i)
{
for(int j(n);j>=0;--j)
bomber[j]=(j>m)?bomber[j-m-1]:0;
for(int j(i);j<=n;++j)
bomber[j]+=bomber[j-i],cal(bomber[j]);
for(int j(0);j<=n;++j)
battleship[j]+=bomber[j],cal(battleship[j]);
}
for(int i(0);i<=n;++i)
nahida+=(1ll*panzer[i]*battleship[n-i])%P,
cal(nahida);
write(nahida,'\n');
flush();
return 0;
}
- 竟然直接压成了一维数组。
- 其实在 \(dp\) 过程中都是用上一个状态加到下一个状态,所以稍加处理便可以压掉一维。好处就是最后统计答案时可以直接利用乘法分配律。