与其被自己的本性牵着走而痛苦,倒不如试着改变自己。

前言

首先自我检讨一下,T1的部分分是原题,但是我这个 FW 居然没有看出来。。(主要是我看错题了)

论语文素养如何限制 OI 水平 别人眼里是一棵树,我眼里是一堆柱子。

T1 玩具

解题思路

预处理 dp[i][j] 表示 i 个点的森林,有 j 个点在第一棵树的概率,转移的时候考虑第 i 个点是否在第一棵子树中。

可以得到状态转移方程:\(dp_{i,j}=dp_{i-1,j-1}\times (j-1)\times inv_i+dp_{i-1,j}\times (i-j)\times inv_i\)

然后用 f[i][j]g[i][j] 分别表示有 i 个点的树或者森林深度不超过 j 的概率。

那么 \(f_{i,j}\) 可以直接从 \(g_{i-1,j-1}\) 转移过来,可以理解为,把森林中所有树的根节点都连为 新加入节点的子节点就好了。

对于 g 数组的转移考虑从一棵树和一个森林中转移:

\[g_{i,j}=\sum\limits_{k=1}^i f_{k,j}\times g_{i-k,j}\times dp_{i,k}
\]

然后在处理时候注意边界问题,以及用之前的状态更新以后的就好了。

code

70pts

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=210;
int n,mod,ans,base=1,f[N][N],c[N][N];
inline int read()
{
int x=0,f=1;
char ch=getchar();
while(ch>'9'||ch<'0')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}
void get_C()
{
for(int i=0;i<N;i++)
{
c[i][0]=c[i][i]=1;
for(int j=1;j<i;j++)
c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
}
}
int ksm(int a,int b)
{
int answer=1;
while(b)
{
if(b&1)
answer=(a*answer)%mod;
a=(a*a)%mod;
b>>=1;
}
return answer%mod;
}
signed main()
{
n=read();
mod=read();
get_C();
f[1][1]=f[2][2]=1;
for(int i=3;i<=n;i++)
for(int j=2;j<=n;j++)
{
for(int p=1;p<=i-2;p++)
for(int q=1;q<=min(j-2,p);q++)
f[i][j]=(f[i][j]+f[p][q]*f[i-p][j]%mod*c[i-2][p-1]%mod)%mod;
for(int p=1;p<=i-1;p++)
for(int q=1;q<=j;q++)
f[i][j]=(f[i][j]+f[p][j-1]*f[i-p][q]%mod*c[i-2][p-1]%mod)%mod;
}
for(int i=1;i<n;i++)
base=base*i%mod;
for(int i=2;i<=n;i++)
ans=(ans+(i-1)*f[n][i]%mod)%mod;
// cout<<ans<<' '<<base<<endl;
printf("%lld",ans*ksm(base,mod-2)%mod);
return 0;
}

正解

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=210;
int n,mod,ans,inv[N],f[N][N],dp[N][N],g[N][N];
inline int read()
{
int x=0,f=1;
char ch=getchar();
while(ch>'9'||ch<'0')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}
void get_inv()
{
inv[0]=inv[1]=1;
for(int i=2;i<=n;i++)
inv[i]=((mod-mod/i)*inv[mod%i])%mod;
}
signed main()
{
n=read();
mod=read();
get_inv();
dp[1][1]=1;
for(int i=2;i<=n;i++)
for(int j=1;j<=i;j++)
dp[i][j]=(dp[i-1][j-1]*(j-1)%mod*inv[i]%mod+dp[i-1][j]*(i-j)%mod*inv[i]%mod)%mod;
for(int i=0;i<=n;i++)
f[1][i]=g[0][i]=1;
for(int i=1;i<=n;i++)
for(int j=0;j<i;j++)
{
g[i][j]=0;
for(int k=1;k<=i;k++)
g[i][j]=(g[i][j]+f[k][j]*g[i-k][j]%mod*dp[i][k]%mod)%mod;
// cout<<g[i][j]<<endl;
f[i+1][j+1]=g[i][j];
for(int k=j+1;k<=n;k++)
f[i+1][k]=f[i+1][j+1];
for(int k=i;k<=n;k++)
g[i][k]=g[i][j];
}
for(int i=2;i<=n;i++)
ans=(ans+(i-1)*(f[n][i-1]-f[n][i-2]+mod)%mod)%mod;
printf("%lld",ans);
return 0;
}

T2 y

解题思路

这里借鉴了一下 cty dalao的做法。

因为空间时间卡的都比较紧,因此我们考虑把距离为 d 的路径掰成两半来考虑

f[i][j][sta]表示走了 i 步,到了 j 点状态为 sta 的情况是否存在。

首先考虑前 \(\dfrac{d}{2}\) 的,因为只能从 1 节点出发,所以边界只能是 \(f_{0,1,0}=true\)

然后就是枚举顺序了,不难发现第一层一定是步数(毕竟我们的 DP 方程都是从前一步转移过来的吗)

接下来点以及它所联通的边,进而枚举每一步状态(注意这里应该是 |而不是直接的=,到达这种状态的方式不只有一种)

对于这一次的每一个结尾点,将它可以有的状态压进一个 vector 数组。

剩下的 \(\dfrac{d}{2}\) 与上面的类似,但是要反着,从 n 个结尾点向前进行更新。

因此,起始点就可以是任意一个点了,所以边界就要把所有的 \(f_{0,i,0}\) 都赋值为 true。

并且和上一次一样把状态压入一个 vector 。

接下来就比较简单了,就是把两段路径合并了,这里需要记录一下。

因为对于两段路径的计算是分离的,并且我们仅仅想要路径的种类数,因此状压的状态的正反无需考虑。

代码的实现细节诶不是很多,注意压 vector 的时候不要写进大循环里,不然就会 TLE ,单独拎出来处理就好了。

code

#include<bits/stdc++.h>
#define int long long
#define f() cout<<"Pass"<<endl;
using namespace std;
inline int read()
{
int x=0,f=1;
char ch=getchar();
while(ch>'9'||ch<'0')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}
const int N=100,M=N*(N-1);
int n,m,dist,ans;
int tot,head[N],nxt[M<<1],ver[M<<1],edge[M<<1];
bool f[15][N][1<<12],f2[15][N][1<<12],vis[1<<22];
vector<int> v[N],v2[N];
void add_edge(int x,int y,int val)
{
ver[++tot]=y;
edge[tot]=val;
nxt[tot]=head[x];
head[x]=tot;
}
signed main()
{
// freopen("date.in","r",stdin);
n=read();
m=read();
dist=read();
for(int i=1,x,y,val;i<=m;i++)
{
x=read();
y=read();
val=read();
add_edge(x,y,val);
add_edge(y,x,val);
}
f[0][1][0]=true;
for(int k=0;k<(dist+1)/2;k++)
for(int i=1;i<=n;i++)
{
for(int j=head[i];j;j=nxt[j])
{
int to=ver[j],col=edge[j];
for(int sta=0;sta<(1<<((dist+1)/2));sta++)
{
f[k+1][to][sta<<1|col]|=f[k][i][sta];
// if(f[k+1][to][sta<<1|col])
// cout<<i<<' '<<to<<' '<<col<<' '<<k<<' '<<sta<<endl;
// cout<<"F1: "<<i<<' '<<to<<' '<<col<<' '<<k<<' '<<sta<<endl;
}
}
}
for(int i=1;i<=n;i++)
for(int sta=0;sta<(1<<((dist+1)/2));sta++)
if(f[(dist+1)/2][i][sta])
v[i].push_back(sta);
for(int i=1;i<=n;i++)
f2[0][i][0]=true;
for(int k=0;k<dist-(dist+1)/2;k++)
for(int i=1;i<=n;i++)
{
for(int j=head[i];j;j=nxt[j])
{
int to=ver[j],col=edge[j];
for(int sta=0;sta<(1<<(dist-(dist+1)/2));sta++)
{
f2[k+1][to][sta<<1|col]|=f2[k][i][sta];
// if(f2[k+1][to][sta<<1|col]) f();
// if(f2[k+1][to][sta<<1|col])
// cout<<i<<' '<<to<<' '<<col<<' '<<k<<' '<<sta<<endl;
// cout<<"F2: "<<i<<' '<<to<<' '<<col<<' '<<k<<' '<<sta<<endl;
}
}
}
for(int i=1;i<=n;i++)
for(int sta=0;sta<(1<<(dist-(dist+1)/2));sta++)
if(f2[dist-(dist+1)/2][i][sta])
v2[i].push_back(sta);
for(int k=1;k<=n;k++)
{
for(int i=0;i<v[k].size();i++)
{
for(int j=0;j<v2[k].size();j++)
{
vis[v[k][i]<<(dist-(dist+1)/2)|v2[k][j]]=true;
}
}
}
for(int i=0;i<(1<<dist);i++)
ans+=vis[i];
printf("%lld",ans);
return 0;
}

T3 z

大坑未补

7.19考试总结(NOIP模拟20)[玩具·y·z]的更多相关文章

  1. [考试总结]noip模拟20

    第五场,再挂分就没了.. 然后就没了.. 考场上一直想方法. 似乎想到了 \(T1\) 正解. 然而几个 \(k\) 的调试信息都让我迷失了自我. 然后有几句啥都没用的语句加在了上面.. 挂分... ...

  2. 2021.8.19考试总结[NOIP模拟44]

    T1 emotional flutter 把脚长合到黑条中. 每个黑条可以映射到统一区间,实际操作就是左右端点取模.长度大于$k$时显然不合法. 然后检查一遍区间内有没有不被黑条覆盖的点即可. 区间端 ...

  3. NOIP 模拟 $20\; \rm y$

    题解 \(by\;zj\varphi\) 首先发现一共最多只有 \(2^d\) 种道路,那么可以状压,(不要 \(dfs\),会搜索过多无用的状态) 那么设 \(f_{i,j,k}\) 为走 \(i\ ...

  4. 6.17考试总结(NOIP模拟8)[星际旅行·砍树·超级树·求和]

    6.17考试总结(NOIP模拟8) 背景 考得不咋样,有一个非常遗憾的地方:最后一题少取膜了,\(100pts->40pts\),改了这么多年的错还是头一回看见以下的情景... T1星际旅行 前 ...

  5. 5.23考试总结(NOIP模拟2)

    5.23考试总结(NOIP模拟2) 洛谷题单 看第一题第一眼,不好打呀;看第一题样例又一眼,诶,我直接一手小阶乘走人 然后就急忙去干T2T3了 后来考完一看,只有\(T1\)骗到了\(15pts\)[ ...

  6. 5.22考试总结(NOIP模拟1)

    5.22考试总结(NOIP模拟1) 改题记录 T1 序列 题解 暴力思路很好想,分数也很好想\(QAQ\) (反正我只拿了5pts) 正解的话: 先用欧拉筛把1-n的素数筛出来 void get_Pr ...

  7. Noip模拟20 2021.7.19

    T1 玩具 题目读错意思直接报零... 拼接方式没读懂以为是个数学题,用卡特兰数,可是的确想多了 数据范围表达出你怎么暴力都行,选择$n^3,dp$ 相当于一片森林,每次多加一条边就合并成一棵树 在$ ...

  8. 2021.9.17考试总结[NOIP模拟55]

    有的考试表面上自称NOIP模拟,背地里却是绍兴一中NOI模拟 吓得我直接文件打错 T1 Skip 设状态$f_i$为最后一次选$i$在$i$时的最优解.有$f_i=max_{j<i}[f_j+a ...

  9. [考试总结]noip模拟23

    因为考试过多,所以学校的博客就暂时咕掉了,放到家里来写 不过话说,vscode的markdown编辑器还是真的很好用 先把 \(noip\) 模拟 \(23\) 的总结写了吧.. 俗话说:" ...

  10. 2021.9.13考试总结[NOIP模拟52]

    T1 路径 考虑每一位的贡献,第$i$位每$2^i$个数会变一次,那么答案为$\sum_{i=1}^{log_2n} \frac{n}{2^i}$. $code:$ 1 #include<bit ...

随机推荐

  1. ES6中新增的Set、Map两种数据结构

    如果要用一句话来描述,我们可以说 Set是一种叫做集合的数据结构,Map是一种叫做字典的数据结构 什么是集合?什么又是字典? 集合是由一堆无序的.相关联的,且不重复的内存结构[数学中称为元素]组成的组 ...

  2. 当服务器间通讯出现No route to host(Host unreachable)

    服务器间通讯出现No route to host(Host unreachable) 背景:因为某些原因,主机房服务器集体重启,其中部署的系统也需要重启,负责的系统是五台服务器,两台用来作为应用的应用 ...

  3. Oracle 查询超级慢之buffer sort

    查询超级慢之buffer sort 在视图中增加了一个临时表作为一个数据源进行id和名称的转换,没加的时候一秒不到,加了以后14秒,感觉有点问题,于是打开了解释计划看了下,发现这个buffer sor ...

  4. Oracle 与当前日期有关的内容

    Oracle 与当前日期有关的内容 求当前日期是周几: 大概就是下面这种方法 to_char(date,'D') Select to_char(date,'ss') from dual取当前时间秒部分 ...

  5. 安装两个jdk Windows系统

    安装两个jdk Windows系统 本来装的是Java 8,现在有一个新的项目要到手上,需要的环境是Java 7,想着还有别的要用,所以就装两个在电脑上 jdk的下载直接去官网就行,不知道怎么下就看这 ...

  6. 力扣383(java&python)-赎金信(简单)

    题目: 给你两个字符串:ransomNote 和 magazine ,判断 ransomNote 能不能由 magazine 里面的字符构成. 如果可以,返回 true :否则返回 false . m ...

  7. 轻松处理高于平常10倍的视频需求,还能节省60%的IT成本,蓝墨做对了什么?

    近年来,Serverless 一直在高速发展,并呈现出越来越大的影响力.主流的云服务商也在不断地丰富云产品体系,提供更好的开发工具,更高效的应用交付流水线,更好的可观测性,更细腻的产品间集成,但一切才 ...

  8. 一文详解SQL关联子查询

    简介: 本文主要介绍什么是关联子查询以及如何将关联子查询改写为普通语义的sql查询. 本文主要介绍什么是关联子查询以及如何将关联子查询改写为普通语义的sql查询. 在背景介绍中我们将讲讲常见的关联子查 ...

  9. 智能数据构建与管理平台Dataphin的前世今生:缘起

    简介: 阿里巴巴提出的OneData方法论帮助企业捋清了数据全生命周期的管理思路,更将其植入到产品Dataphin(智能数据构建与管理)中,通过阿里云为企业提供服务. Dataphin 智能数据构建与 ...

  10. Ollama是什么,为什么这么好用

    Ollama 是一个开源框架,专为在本地机器上便捷部署和运行大型语言模型(LLM)而设计. 以下是其主要特点和功能概述: 1. 简化部署:Ollama 目标在于简化在 Docker 容器中部署大型语言 ...