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]; ...
随机推荐
- 更好的滚动体验>better-scroll
认识better-scroll better-scroll是一款重点用于解决移动端(已支持PC)各种滚动场景需求的插件,可使页面滚动效果更加流畅且富有弹性 better-scroll是用纯JavaSc ...
- opencv打开摄像头获取视频程序
// // main.cpp // opencv3 // // Created by PKU on 14-9-16. // Copyright (c) 2014年 PKU. All rights re ...
- 2020BUAA 软工-结对作业
结对作业 项目 内容 北航2020软工 班级博客 作业要求 具体要求 1.在文章开头给出教学班级和可克隆的 GitHub 项目地址(例子如下).(1') 教学班级 005 项目地址 GitHub 2. ...
- JSX语法详解
一.基础1.JSX是什么JSX是一种像下面这样的语法: const element = <h1>Hello, world!</h1>;1它是一种JavaScript语法扩展,在 ...
- 解密华为云FusionInsight MRS新特性:一架构三湖
摘要:华为云安全网关产品总监郭冕在"华为云TechWave云原生2.0专题日"上发表<华为云FusionInsight MRS,一个架构实现三种数据湖>的主题演讲,分享 ...
- python基础之内建模块base64
一.Base64概念 什么是Base64? 按照RFC2045的定义,Base64被定义为:Base64内容传送编码被设计用来把任意序列的8位字节描述为一种不易被人直接识别的形式.(The Base6 ...
- 016.Python闭包函数以及locals和globals
一 闭包函数 内函数使用了外函数的局部变量,并且外函数把内函数返回出来的过程叫做闭包,这个内函数叫做闭包函数 1.1 闭包函数语法 def outer(): a = 5 def inner(): pr ...
- HTML中option的单页调用
我们在用到下拉列表框select时,需要对选中的<option>选项触发事件,其实<option>本身没有触发事件方法,我们只有在select里的 onchange方法里触发. ...
- IDEA 最实用快捷键【MAC版本】
目录 option + F7 Ctrl + B / Ctrl +鼠标左键(一键两用,可以无限循环的跳过来跳过去,我跳过去了,我又跳回去了) command + E (这个快捷键很有用,为什么我老是用不 ...
- GO学习-(21) Go语言基础之Go性能调优
Go性能调优 在计算机性能调试领域里,profiling 是指对应用程序的画像,画像就是应用程序使用 CPU 和内存的情况. Go语言是一个对性能特别看重的语言,因此语言中自带了 profiling ...