NOIP模拟测试15「建造城市city(插板法)·轰炸·石头剪刀布」
建造城市
题解
先思考一个简单问题
10个$toot$ 放进5间房屋,每个房屋至少有1个$toot$,方案数
思考:插板法,$10$个$toot$有$9$个缝隙,$5$间房屋转化为$4$个挡板,放在toot缝隙之间得到$C_{9}^{4}$
10个$toot$ 放进$5$间房屋,每个房屋里可以没有$toot$,方案数
思考:插板法使用条件必须是每组至少有1个,那么我们事先在每个房屋中放一个$toot$变成$15$个$toot$放进$5$个房屋,可以插板法,与上一题类似$C_{14}^{4}$
那么应用到这个题上呢?
这个题$toot$加了一个上界,非常棒对不对,我们可以考虑容斥
怎么算$\sum\limits_{i=0}^{i<=n} {-1}^i *C_{n}^{i}*C_{m-i*k-1}^{n-1}$
前面是枚举的多的城市,后面枚举的是多的,怎么理解呢?
首先我们让$i$个城市提前选出$i*k$ 个 $toot$,假设我们当前是$i==0$,那么我们选出的包含0个不符合情况的,1个不符合情况的,2个不符合情况的,3个不符合情况的------n个
我们只需要简单容斥一下就完了
代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define A 10101010
#define mod 998244353
ll ni[A],jie[A];
ll n,m,k;
ll meng(ll x,ll k){
ll ans=1;
for(;k;k>>=1,x=x*x%mod)
if(k&1)
ans=ans*x%mod;
return ans;
}
ll C(ll n,ll m){
return jie[n]*ni[n-m]%mod*ni[m]%mod;
}
int main(){
jie[0]=1;
for(ll i=1;i<=10000000;i++)
jie[i]=jie[i-1]*i%mod;
ni[10000000]=meng(jie[10000000],mod-2);
for(ll i=9999999;i>=1;i--)
ni[i]=ni[i+1]*(i+1)%mod;
ni[0]=1;
/* while(1){
ll a,b;
scanf("%lld%lld",&a,&b);
cout<<C(a,b)<<endl;
}
*/ scanf("%lld%lld%lld",&n,&m,&k);
ll ans=0,tmp=1;
for(ll i=0;i<=n;i++){
if(n-1>m-i*k-1) break;
ans=(ans+tmp*C(n,i)*C(m-i*k-1,n-1)%mod)%mod;
tmp=-tmp;
}
cout<<(ans+mod)%mod<<endl;
}
轰炸
语文不好被坑了
一句话题解
tarjan缩点+最长链
代码不放了
石头剪刀布
很奇妙的思路,但觉的现在还是似懂非懂的。
首先这个题是先计算所有概率,最后再统计贡献
和以往做的期望题不太一样
这个题没有顺序很恶心,思考换一种方法避免无序带来影响
我们发现如果只开三维,最后根本无法统计答案,统计起来会像一坨
尝试开四维?
$f[i][j][k][4]$
$f[i][j][k][1]$表示已经有$i$个人出石头,$j$个人出剪刀,$k$个人出布,下一轮出石头
$f[i][j][k][2]$表示已经有$i$个人出石头,$j$个人出剪刀,$k$个人出布,下一轮出剪刀
$f[i][j][k][3]$表示已经有$i$个人出石头,$j$个人出剪刀,$k$个人出布,下一轮出布
这样答案转移起来就很好转移了
$ans=\sum\limits _{i=1}^{i<=n} max(f[i][j][k][1]+f[i][j][k][2]*3,f[i][j][k][2]+f[i][j][k][3]*3,f[i][j][k][3]+f[i][j][k][1]*3)$
思考状态转移
直接转移肯定难以转移,开辅助数组$g[i][j][k]$表示出$i$个石头,$j$个剪刀,$k$个布的概率
显然我们可以得到
$f[i][j][k][u]=f[i-1][j][k][u]*x[o][1]+f[i][j-1][k][u]*x[o][2]+f[i][j][k-1][u]*x[o][3]+(x[o][1]+x[o][2]+x[o][3])*g[i][j][k]$
思考这是什么意思
拿1举例,你当前状态已经这样,你又往里面放了一个人,他出了石头,那么当前概率就等于之前的概率*转移过来的概率
下面是迪哥解释
//当u>0时就不太一样了,计算的是接下来出1的概率
//它由上一轮对方出1的概率乘对方真的出了1的概率累加而来,此时i+j+k!=s
//因为你把这玩意当成一个背包不断往里面放对手来更新其概率
//意思大概就是“目前的状态已经是那样了而且下一轮你遇到了s”,然后s对你的概率产生的贡献
//所以就是你走到原状态的概率,乘上s出1的概率,就是s对目前状态的概率贡献
//所以i+j+k==s时不能枚举到3,因为相当于你的原状态里面已经有s个人了,可是你现在刚刚开始考虑第s个人啊
细节
你最后统计答案时不能枚举到n因为n没有下一个人了
吴迪带注释代码
#include<bits/stdc++.h>
using namespace std;
//首先题意可能还有人理解错了。题目的意思是你要根据对手分别出了几个石头几个剪刀来决策
//而并不是一场战斗结束后你就能知道对方具体是谁从而直接推断剩下的人
#define d(x,k) for(int x=k;x>=0;--x)//压行,字少
int n;double x[51][4],f[51][51][51][4],ans,c[51][51];
//f数组的含义:当最后一维为1~3时表示第i+j+k+1个人在前面的人出了i个1,j个2,k个3的情况下出1~3的概率
//当最后一维为0时表示前i+j+k个人出了i个1,j个2,k个3的概率,即那个题解里的g数组
int main(){
scanf("%d",&n); f[0][0][0][0]=1;//初始化
for(int i=1;i<=n;++i) scanf("%lf%lf%lf",&x[i][1],&x[i][3],&x[i][2]),
x[i][1]/=300,x[i][2]/=300,x[i][3]/=300;
//读入概率,注意顺序是132。把石头剪刀步分别抽象为123,故1胜2,2胜3,3胜1
for(int i=0;i<=50;++i) c[i][0]=1;
for(int i=1;i<=50;++i) for(int j=1;j<=i;++j) c[i][j]+=c[i-1][j-1]+c[i-1][j];
//杨辉三角。注意:要用到50!级别的而没有取模,所以要开long long或double
for(int s=1;s<=n;++s) d(i,s) d(j,s-i) d(k,s-i-j) d(u,(i+j+k==s?0:3)){//有点像个背包
//你可以把s单独再开一维的数组来表示目前考虑到第几个人,更好理解但貌似会炸内存
//u为1~3时,分别枚举第几个人,前面的人出过几个1,2,3,这个人要出u
//注意u的枚举是当i+j+k!=s时才更新对方下一次出123的概率,否则只更新到达某状态的概率
//u为0时,计算到达这个状态的总概率(即题解中的g数组)
if(i)f[i][j][k][u]+=f[i-1][j][k][u]*x[s][1];//这个人s出了1,累加概率
//当u=0时,f[i][j][k][0]由f[i-1][j][k][0]转移而来(u=0并不考虑下一个人会出什么)
//在原状态出一个1即为新状态,后者的概率为x[s][1]。计算g数组就不必考虑其他f值的影响
//因为根据含义就有f[i][j][k][0]=f[i][j][k][1]+f[i][j][k][2]+f[i][j][k][3]
//当u>0时就不太一样了,计算的是接下来出1的概率
//它由上一轮对方出1的概率乘对方真的出了1的概率累加而来,此时i+j+k!=s
//因为你把这玩意当成一个背包不断往里面放对手来更新其概率
//意思大概就是“目前的状态已经是那样了而且下一轮你遇到了s”,然后s对你的概率产生的贡献
//所以就是你走到原状态的概率,乘上s出1的概率,就是s对目前状态的概率贡献
//所以i+j+k==s时不能枚举到3,因为相当于你的原状态里面已经有s个人了,可是你现在刚刚开始考虑第s个人啊
if(j)f[i][j][k][u]+=f[i][j-1][k][u]*x[s][2];//出2,同上
if(k)f[i][j][k][u]+=f[i][j][k-1][u]*x[s][3];//出3,同
if(u)f[i][j][k][u]+=f[i][j][k][0]*x[s][u];
//这个就是弥补了上面的缺陷。本层转移。不管目前的状态是什么,反正第s个人就是出u了
//与上面的并不重复。一个是在说s对以前的状态的贡献,这个是在说s对当前状态的贡献
}
d(i,n-1) d(j,n-1-i) d(k,n-i-j-1)//i+j+k不要枚举到n,因为已经进行过n轮后下一次再出什么已经不重要不记分了
ans+=max(max(f[i][j][k][1]+3*f[i][j][k][2],f[i][j][k][2]+3*f[i][j][k][3]),f[i][j][k][3]+3*f[i][j][k][1])/c[n][i+j+k]/(n-i-j-k);
//在每一种状态下(即确定对手已经出了i个1,j个2,k个3)时你都有唯一确定的最优决策来进行下一轮
//每一次决策时都会累加分数,3种决策分别对应出1,2,3.f[i][j][k][1]即为与1打平,3*f[i][j][k][2]即为战胜2
//你所说的最优决策就是根据已有信息(每个对手出了什么),通过猜测对手下一步会出什么来权衡3中决策
//至于为什么用到了组合数:因为你所算的概率只是到达这一步的概率,但是你是从n个人里随便选出了c[n][i+j+k]个人
//然而其实在同一场游戏中对于同样的i+j+k你只会选1次,在计算的时候你把概率累加在一起了,现在要求一个平均值
//再除一个(n-i-j-k)的原因也差不多,因为你是要从剩下的(n-i-j-k)个人里选出一个去挑战
//这一步的概率是1/(n-i-j-k),然而你在上面5层循环的时候并没有考虑,所以在这里统一除去
printf("%.12lf\n",ans);//给的std里是用%.12f输出double的,真是惊奇
}//把注释全删掉你就会发现这个代码只有21行811B
我这个菜鸡的代码
#include<bits/stdc++.h>
using namespace std;
#define A 52
#define ll long long
double f[A][A][A][5],x[A][5];
double ans=0;
ll C[A][A],n;
int main(){
f[0][0][0][0]=1;
scanf("%lld",&n);
for(ll i=0;i<=n;i++)
C[i][0]=1;
for(ll i=1;i<=n;i++)
for(ll j=1;j<=i;j++)
C[i][j]=C[i-1][j]+C[i-1][j-1];
for(ll i=1;i<=n;i++)
scanf("%lf%lf%lf",&x[i][1],&x[i][3],&x[i][2]),
x[i][1]/=300,x[i][3]/=300,x[i][2]/=300;
for(ll o=1;o<=n;o++){
for(ll i=o;i>=0;i--){
for(ll j=o-i;j>=0;j--){
for(ll k=o-i-j;k>=0;k--){
for(ll u=(((i+j+k)==o)?0:3);u>=0;u--){
if(i)
f[i][j][k][u]+=f[i-1][j][k][u]*x[o][1];
if(j)
f[i][j][k][u]+=f[i][j-1][k][u]*x[o][2];
if(k)
f[i][j][k][u]+=f[i][j][k-1][u]*x[o][3];
if(u)
f[i][j][k][u]+=f[i][j][k][0]*x[o][u];
}
}
}
}
}
for(ll i=n-1;i>=0;i--)
for(ll j=n-1-i;j>=0;j--)
for(ll k=n-1-i-j;k>=0;k--){
ans+=max(max(f[i][j][k][1]+f[i][j][k][2]*3,f[i][j][k][2]+f[i][j][k][3]*3),f[i][j][k][3]+f[i][j][k][1]*3)/C[n][i+j+k]/(n-i-k-j);
}
printf("%.12lf\n",ans);
}
NOIP模拟测试15「建造城市city(插板法)·轰炸·石头剪刀布」的更多相关文章
- NOIP模拟测试24「star way to hevaen·lost my music」
star way to heaven 题解 大致尝试了一下并查集,记忆化搜索,最小生成树 最小生成树是正解,跑最小生成树然后找到最大的值 欧几里德距离最小生成树学习 prim楞跑 至于为什么跑最小生成 ...
- NOIP模拟测试13「矩阵游戏·跳房子·优美序列」
矩阵游戏 考试时思路一度和正解一样,考试到最后还是打了80分思路,结果80分打炸了只得了40分暴力分 题解 算出来第一列的总值,每次通过加每两列之间的差值得出下一列的总值 算第一列我们只需要让当前点* ...
- 2019.8.9 NOIP模拟测试15 反思总结
日常爆炸,考得一次比一次差XD 可能还是被身体拖慢了学习的进度吧,虽然按理来说没有影响.大家听的我也听过,大家学的我也没有缺勤多少次. 那么果然还是能力问题吗……? 虽然不愿意承认,但显然就是这样.对 ...
- NOIP模拟测试19「count·dinner·chess」
反思: 我考得最炸的一次 怎么说呢?简单的两个题0分,稍难(我还不敢说难,肯定又有人喷我)42分 前10分钟看T1,不会,觉得不可做,完全不可做,把它跳了 最后10分钟看T1,发现一个有点用的性质,仍 ...
- 8.9 NOIP模拟测试15 建设城市(city)+轰炸行动(bomb)+石头剪刀布(rps)
鉴于T3的惨烈程度,我决定先来颓篇题解. T1 建设城市(city) 挡板法+容斥 m个建设队分成n组,每组必须有一个,先不考虑上限,共有 C(m-1,n-1)种方案. 有i个组是超过k个的,容斥掉 ...
- NOIP模拟测试3「序列·熟练剖分·建造游乐园(play)」
---恢复内容开始--- 序列 刚调出来样例就A了,假装是水题. 因为是乱序,我们要求出来每两项之间最小公比,而不是直接比 求出来每两项之间最小公比,然后扫一遍就完了.(还要注意重复情况) 那么问题就 ...
- NOIP模拟测试30「return·one·magic」
magic 题解 首先原式指数肯定会爆$long$ $long$ 首先根据欧拉定理我们可以将原式换成$N^{\sum\limits_{i=1}^{i<=N} [gcd(i,N)==1] C_{G ...
- NOIP模拟测试21「折纸·不等式」
折纸 题解 考试时无限接近正解,然而最终也只是接近而已了 考虑模拟会爆炸,拿手折纸条试一试,很简单 考你动手能力 代码 #include<bits/stdc++.h> using name ...
- NOIP模拟测试18「引子·可爱宝贝精灵·相互再归的鹅妈妈」
待补 引子 题解 大模拟,注意细节 代码1 #include<bits/stdc++.h> using namespace std; int n,m;char a[1005][1005]; ...
随机推荐
- Charles的功能(web)
# 验证是否可以获取web端的https接口 1. 打开Charles 2.打开游览器输入数据 3. 查看Charles 4.从上图所看,能获取htpps的包数据,即可对web端进行抓包 4.char ...
- OPC使用思路
- 17.继承 and18.接口和多态 内部类 匿名内部类,Lambda表达式
1. 继承 1.1 继承的实现(掌握) 继承的概念 继承是面向对象三大特征之一,可以使得子类具有父类的属性和方法,还可以在子类中重新定义,以及追加属性和方法 实现继承的格式 继承通过extends实现 ...
- ruby基础(二)
ruby语法基础 1.方法 方法时对象定义的与该对象相关的操作.在Ruby中,对象的所有的操作都被封装成 方法. 语法糖:语法糖是一种为了照顾一般人的习惯而产生的特殊语法. ruby中一切数据都是对象 ...
- CentOS7启动SSH服务报:Job for ssh.service failed because the control process exited with error code
CentOS7启动SSH服务报:Job for ssh.service failed because the control process exited with error code....... ...
- X Sever —— Xorg
X Sever -- Xorg 发表于 2020-03-20 分类于 系统服务 , Xorg 阅读次数:39 阅读次数:48 本文字数: 7k 阅读时长 ≈ 6 分钟 Xorg:基于X11协议的服务 ...
- 二进制部署K8S-1基本概念
二进制部署K8S-1基本概念 感谢老男孩教育王导的公开视频,文档整理自https://www.yuque.com/duduniao/k8s. 1.实验环境 1.1 虚拟机 因为在后期运行容器需要有大量 ...
- 像素 PIXEL 图片的基本单位 像素非常小 图片是成千上万的像素组成 显示/屏幕分辨率 (DPI 屏幕分辨率)
像素 PIXEL 图片的基本单位 像素非常小 图片是成千上万的像素组成 显示/屏幕分辨率 (DPI 屏幕分辨率) 图像分辨率 (PPI) 1920*1080是像素点长度1920个像素点 X1080个像 ...
- keil使用VScode外部编辑器
1.首先我们双击桌面的keil图标,打开keil主界面: 2.点击上方菜单栏的Tools菜单,选择如下图所示的选项: 3.点击如下图所示的菜单上红笔标注的地方,给这个工具命名为vscode: 4.然后 ...
- make clean 和 make distclean区别-(转自秋水Leo)
make clean仅仅是清除之前编译的可执行文件及配置文件. 而make distclean要清除所有生成的文件. Makefile 在符合GNU Makefiel惯例的Makefile中,包含了一 ...