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

前言

首先自我检讨一下,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. 实训篇-Html-多媒体标签

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...

  2. Go 单元测试之mock接口测试

    目录 一.gomock 工具介绍 二.安装 三.使用 3.1 指定三个参数 3.2 使用命令为接口生成 mock 实现 3.3 使用make 命令封装处理mock 四.接口单元测试步骤 三.小黄书Se ...

  3. web常见的攻击方式有哪些?如何防御?

    一.是什么 Web攻击(WebAttack)是针对用户上网行为或网站服务器等设备进行攻击的行为 如植入恶意代码,修改网站权限,获取网站用户隐私信息等等 Web应用程序的安全性是任何基于Web业务的重要 ...

  4. ASP.NET Core Web API下基于Keycloak的多租户用户授权的实现

    在上文<Keycloak中授权的实现>中,以一个实际案例介绍了Keycloak中用户授权的设置方法.现在回顾一下这个案例: 服务供应商(Service Provider)发布/Weathe ...

  5. Flink SQL 性能优化:multiple input 详解

    简介: 在 Flink 1.12 中,针对目前 operator chaining 无法覆盖的场景,推出了 multiple input operator 与 source chaining 优化.该 ...

  6. Service Mesh 从“趋势”走向“无聊”

    简介: 过去一年,阿里巴巴在 Service Mesh 的探索道路上依旧扎实前行,这种坚定并非只因坚信 Service Mesh 未来一定是云计算基础技术的关键组成部分,还因需要借这一技术趋势去偿还过 ...

  7. Snowflake如日中天是否代表Hadoop已死?大数据体系到底是什么?

    ​简介: 本文作者关涛是大数据系统领域的资深专家,在微软(互联网/Azure云事业群)和阿里巴巴(阿里云)经历了大数据发展20年过程中的后15年.本文试从系统架构的角度,就大数据架构热点,每条技术线的 ...

  8. [FAQ] Vmmem 内存占用高的问题 -Win10 -WLS2

    1按下Windows + R 键,输入 %UserProfile% 并运行进入用户文件夹 2新建文件 .wslconfig ,然后记事本编辑 3 填入以下内容并保存, memory为系统内存上限,这里 ...

  9. [ML] 科学编程语言 Octave 简单操作

    octave 是和 matlab 类似的软件,可以方便的进行矩阵计算.图形绘图. matlab 收费,octave 是 gnu 开源软件. Mac 安装: $ brew install octave ...

  10. K8s集群中部署SpringCloud在线购物平台(一)

    一.安装k8s高可用集群 主机名 IP 配置 网络 master控制节点 192.168.10.10 centos 7.9 4核4G 桥接 node1工作节点 192.168.10.11 centos ...