被拉到郑州培训了

考了一上午莫名自闭

帮助慎老师拿到\(rk1\)非常开心

简述一下题目吧

T1.まんふは函数

原题地址

考原题还行

据说是\(Huffman\)树

在成爷爷的再三讲解下,我终于明白了

我们可以把\(f(i,j)\)理解为合并了\(n-i+1\)个点,形成了\(j\)棵树这个状态到最终状态也就是\(n\)个点\(1\)棵树的最小代价

于是来思考一下这个方程

\[f(i,j)=min\{f(i-1,j+1),f(i,ceil(j/2)+b_i)\}
\]

第一个方程是新增加了一个节点,这个节点独立作为了一棵树,所以没有什么算贡献的必要

第二个方程比较\(nb\)了就是把现在已经形成的\(j\)棵\(Huffman\)树两两合并,之后算贡献,因为是从\(n\)往前合并的,于是合并一次的话花费是后面\(n-i+1\)个数的和,也就是\(b_i\)

于是问题等价于把这\(n\)个元素合并成一棵树的最小代价

这个的话,不知道合并果子您做过没有

T2.穿越广场

【问题描述】

$L $国的仪仗队要穿越首都广场了。

首都广场可以看做是一块 \(N*M\) 的矩形网 格,仪仗队要从左上角的格点\((0,0)\)行进到右下角的格点\((N,M)\),行进过程中只能 向右走或者向下走。

如果把向右走记为\(R\),把向下走记为\(D\),则仪仗队的 行进序列是一个包含 $M \(个\)R$和 \(N\) 个\(D\)的字符串。

这时,$L $国的首长又提出了一个奇葩的要求。他认为仪仗队行走的序列中必 须包含他给出的两个字符串。请你计算一下,满足首长要求的行进序列有多少种 呢?

【输入格式】

从文件 \(square.in\) 中读入数据。 第一行一个整数 \(T\),表示数据组数。 每组数据的第一行是两个整数 \(M\)、\(N\),表示行进序列由 \(M\) 个\(R\)和 \(N\) 个$ D$构成。 每组数据的第二行和第三行是两个不相同的字符串,表示首长要求这两个字 符串是行进序列的子串。

【输出格式】

输出到文件$ square.out$ 中。 一个整数,表示满足要求的行进序列的数量模 $1000000007 $的值。

【样例输入 1】

2

3 2

RRD

DDR

3 2

R

D

【样例输出 1】

1

10

【数据规模与约定】

对于 \(20\%\)的数据,字符串长度\(<=2\)。

对于 \(50\%\) 的数据,\(1<=N,M<=50\),字符串长度\(<=50,T=1\)。

对于 \(100\%\) 的数据,\(1<=N,M<=100\),字符串由\(R\)、\(D\)组成且长度\(<=100\), \(1<=T<=10\)。

这个\(t2\)就不是原题了,但是还是非常套路的

先看一下\(50\)分的做法,我们甚至可以来一个\(O(n^4)\)的做法

非常显然我们可以设\(dp[i][j][k][p]\)表示走到了\((i,j)\)这个格子,在第一个串里匹配到了\(k\)位置,在第二个串里匹配到了\(p\)位置的方案数

转移的话我们需要枚举下一步是走\(D\)还是\(R\),之后更新出新的匹配位置,这个需要我们提前处理好第一个串和第二个串的\(next\)数组,之后每次转移就像\(kmp\)那样匹配就好了

代码

#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#define re register
#define maxn 80
#define LL long long
#define inf 999999999
#define max(a,b) ((a)>(B)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
const int mod=1e9+7;
inline int read()
{
char c=getchar();int x=0;while(c<'0'||c>'9') c=getchar();
while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
char S[2][maxn];
short nx[2][maxn];
short n,m,len[2],T;
int dp[maxn][maxn][maxn][maxn];
inline void getnx(int o)
{
memset(nx[o],0,sizeof(nx[o]));
nx[o][1]=0;
len[o]=strlen(S[o]+1);
for(re int i=2;i<=len[o];i++)
{
int p=nx[o][i-1];
while(p&&S[o][p+1]!=S[o][i]) p=nx[o][p];
if(S[o][p+1]==S[o][i]) nx[o][i]=p+1;else nx[o][p]=0;
}
}
int main()
{
T=read();
while(T--)
{
m=read(),n=read();
scanf("%s",S[0]+1),scanf("%s",S[1]+1);
getnx(0),getnx(1);
memset(dp,0,sizeof(dp));
dp[0][0][0][0]=1;
for(re int i=0;i<=n;i++)
for(re int j=0;j<=m;j++)
for(re int k=0;k<=len[0];k++)
for(re int p=0;p<=len[1];p++)
{
if(!dp[i][j][k][p]) continue;
int kk=0,pp=0;
if(k==len[0]) kk=k;
if(p==len[1]) pp=p;
if(i!=n)
{
if(!kk)
{
kk=k;
while(kk&&S[0][kk+1]!='D') kk=nx[0][kk];
if(S[0][kk+1]=='D') kk++;
else kk=0;
}
if(!pp)
{
pp=p;
while(pp&&S[1][pp+1]!='D') pp=nx[1][pp];
if(S[1][pp+1]=='D') pp++;
else pp=0;
}
dp[i+1][j][kk][pp]=(dp[i+1][j][kk][pp]+dp[i][j][k][p])%mod;
}
kk=0,pp=0;
if(k==len[0]) kk=k;
if(p==len[1]) pp=p;
if(j!=m)
{
if(!kk)
{
kk=k;
while(kk&&S[0][kk+1]!='R') kk=nx[0][kk];
if(S[0][kk+1]=='R') kk++;
else kk=0;
}
if(!pp)
{
pp=p;
while(pp&&S[1][pp+1]!='R') pp=nx[1][pp];
if(S[1][pp+1]=='R') pp++;
else pp=0;
}
dp[i][j+1][kk][pp]=(dp[i][j+1][kk][pp]+dp[i][j][k][p])%mod;
}
}
printf("%d\n",dp[n][m][len[0]][len[1]]);
}
return 0;
}

发现我们记录两个串的匹配位置真是太奢侈了,我们考虑把这两个串的信息整合一下

发现我们只需要开一个\(AC\)自动机就好了呀

于是设\(dp[i][j][k][0/1/2/3]\)表示到格子\((i,j)\)在自动机上走到了\(k\)位置,匹配的状态是\(0/1/2/3\)这些个二进制数

我们发现这个样子就非常好转移了,每次需要转移的话直接利用\(son[k][R]\)或\(son[k][D]\)同时维护出结束标记就好了

代码

#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#define re register
#define maxn 105
#define LL long long
#define inf 999999999
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
const int mod=1e9+7;
inline int read()
{
char c=getchar();int x=0;while(c<'0'||c>'9') c=getchar();
while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
char S[2][maxn];
int n,m,T,len[2],cnt;
int son[maxn+maxn][2],flag[maxn+maxn],fail[maxn+maxn];
int dp[maxn][maxn][maxn+maxn][4];
inline void ins(int o)
{
len[o]=strlen(S[o]+1);
int now=0;
for(re int i=1;i<=len[o];i++)
if(S[o][i]=='D') S[o][i]=0;else S[o][i]=1;
for(re int i=1;i<=len[o];i++)
{
if(!son[now][S[o][i]]) son[now][S[o][i]]=++cnt;
now=son[now][S[o][i]];
}
flag[now]|=(1<<(o));
}
inline void Build()
{
std::queue<int> q;
for(re int i=0;i<2;i++) if(son[0][i]) q.push(son[0][i]);
while(!q.empty())
{
int k=q.front();q.pop();
flag[k]|=flag[fail[k]];
for(re int i=0;i<2;i++)
if(son[k][i]) fail[son[k][i]]=son[fail[k]][i],q.push(son[k][i]);
else son[k][i]=son[fail[k]][i];
}
}
int main()
{
freopen("square.in","r",stdin);
freopen("square.out","w",stdout);
T=read();
while(T--)
{
m=read(),n=read();
scanf("%s",S[0]+1),scanf("%s",S[1]+1);
memset(dp,0,sizeof(dp)),memset(son,0,sizeof(son)),memset(flag,0,sizeof(flag)),memset(fail,0,sizeof(fail));
cnt=0,ins(0),ins(1),Build();
dp[0][0][0][0]=1;
for(re int i=0;i<=n;i++)
for(re int j=0;j<=m;j++)
for(re int k=0;k<=cnt;k++)
for(re int p=0;p<4;p++)
if(dp[i][j][k][p])
{
if(i!=n)
{
int kk=son[k][0];
dp[i+1][j][kk][p|flag[kk]]=(dp[i+1][j][kk][p|flag[kk]]+dp[i][j][k][p])%mod;
}
if(j!=m)
{
int kk=son[k][1];
dp[i][j+1][kk][p|flag[kk]]=(dp[i][j+1][kk][p|flag[kk]]+dp[i][j][k][p])%mod;
}
}
int ans=0;
for(re int i=0;i<=cnt;i++) ans=(ans+dp[n][m][i][3])%mod;
printf("%d\n",ans);
}
return 0;
}

T3.存印器

【问题描述】

一个存印器是包含 \(M\) 个变量并且可以接受两种指令的机器,这两种指令分 别为:

  1. \(variable=integer\)

  2. \(print(variable)\)

\(variable\) 可以用 \(M\) 个变量中的任意一个变量的名称替换,变量名称用一个小 写字母表示。\(integer\) 可以用任意整数替换。

$print $打印出变量中当前存储的值。 存印器执行一次变量赋值操作需要耗费的代价为 \(integer\) 转化为二进制数后 包含 $1 $的个数。执行打印操作不耗费代价。

现在有一个长度为$ N$ 的整数序列需要打印。如果用存印器按顺序打印这个序 列,至少需要多少代价呢?

【输入格式】

从文件$ saveprint.in$ 中读入数据。 第一行两个整数 \(N,M\)。 第二行 \(N\) 个整数,表示需要打印的序列。

【输出格式】

输出到文件$ saveprint.out$ 中。 输出一个整数表示最小代价。

【样例输入 1】

7 2

1 2 2 4 2 1 2

【样例输出 1】

4

【数据规模与约定】

对于 \(20\%\)的数据,\(1≤n≤10\)。

对于 \(50\%\)的数据,\(1≤m≤2\)。

对于 \(100\%\)的数据,\(1≤n≤250\), \(1≤m≤26\),序列中的整数在\(1-10^9\)范围 内。

写了一个\(50\)的\(m=2\)的\(dp\)拿了\(60\)非常开心

\(dp\)太傻了就不说了

这题正解一看就是网络流啊,而且一看就是费用流

发现这个其实和某一道最小权路径覆盖一模一样啊

我们把每个点\(i\)拆成\(i\)和\(i'\)两个点,之后搞一个超级源点\(S\)向每一个\(i\)连一条容量为\(1\)费用为\(0\)的边,\(i'\)向\(T\)连容量为\(1\)费用为\(0\)的边

之后每个点\(i\)向\((i+1)',(i+2)'...n'\)连容量为\(1\)费用为\(bit\)的边,\(bit\)为指向的点的二进制中\(1\)的个数,如果这条边连接的是两个权值相同的点那么这条边的费用为\(0\),这样就可以表示存印器里权值的切换

之后\(S\)向\(S'\)连容量为\(m\)的边,\(S'\)向所有的\(i'\)连边,容量为\(1\),费用为对应的\(bit\),表示存印器刚开始被存入了这个权值

代码就不写了,就是一个费用流的板子了

郑州集训day1自闭有感的更多相关文章

  1. 2019暑期金华集训 Day1 组合计数

    自闭集训 Day1 组合计数 T1 \(n\le 10\):直接暴力枚举. \(n\le 32\):meet in the middle,如果左边选了\(x\),右边选了\(y\)(且\(x+y\le ...

  2. 2019暑期金华集训 Day1 数据结构

    自闭集训 Day1 数据结构 CF643G 用类似于下面的方法,搬到线段树上. 如何合并两个集合?先全部放在一起,每次删掉最小的\(cnt_i\),然后把其他所有的\(cnt\)都减去\(cnt_i\ ...

  3. 【欧拉回路+最小生成树】SD开车@山东2018省队一轮集训day1

    目录 [欧拉回路+最小生成树]SD开车@山东2018省队一轮集训day1 PROBLEM 题目描述 输入 输出 样例输入 样例输出 提示 SOLUTION CODE [欧拉回路+最小生成树]SD开车@ ...

  4. 国庆集训 Day1 T2 生成图 DP

    国庆集训 Day1 T2 生成图 现在要生成一张\(n\)个点的有向图.要求满足: 1.若有 a->b的边,则有 b->a 的边 2.若有 a->b 的边和 b->c 的边,则 ...

  5. LOJ 6060「2017 山东一轮集训 Day1 / SDWC2018 Day1」Set(线性基,贪心)

    LOJ 6060「2017 山东一轮集训 Day1 / SDWC2018 Day1」Set $ solution: $ 这一题的重点在于优先级问题,我们应该先保证总和最大,然后再保证某一个最小.于是我 ...

  6. 牛客2018国庆集训 DAY1 D Love Live!(01字典树+启发式合并)

    牛客2018国庆集训 DAY1 D Love Live!(01字典树+启发式合并) 题意:给你一颗树,要求找出简单路径上最大权值为1~n每个边权对应的最大异或和 题解: 根据异或的性质我们可以得到 \ ...

  7. 暑假集训Day1 整数划分

    题目大意: 如何把一个正整数N(N长度<20)划分为M(M>=1)个部分,使这M个部分的乘积最大.N.M从键盘输入,输出最大值及一种划分方式. 输入格式: 第一行一个正整数T(T<= ...

  8. 考前停课集训 Day1 废

    [友情链接] Day1 今天模拟赛倒数…… 感觉自己菜到爆炸…… 被一个以前初一的倒数爆踩…… 感觉自己白学了. 满分400,自己只有100.真的是倒数第一…… 做了一个T2,其他暴力分全部没有拿到… ...

  9. NOIP2017 国庆郑州集训知识梳理汇总

    第一天 基础算法&&数学 day1难度测试 如果要用一个词来形容上午的测试,那真是体无完肤.  成绩: 题目 成绩 评价 T1 50 一般 T2 10 大失所望 T3 0 差 基础算法 ...

随机推荐

  1. rancher 2.X 搭建小型web集群+mysql主从复制

    一,环境配置    rancher 2.1.6 二,配置harbor私有仓库 见上文 三,配置私有镜像 01,总文件 dockerfile 为主配置文件,html 为站点文件wordpress.,官网 ...

  2. (转)用shell脚本实现杨辉三角的4个实例!

    概述:    中国古代数学家在数学的许多重要领域中处于遥遥领先的地位.中国古代数学史曾经有自己光辉灿烂的篇章,而杨辉三角的发现就是十分精彩的一页.杨辉三角形,是二项式系数在三角形中的一种几何排列.杨辉 ...

  3. Oracle命令整理

    1 常用命令 常用命令 1 sqlplus  scott/tiger@192.168.47.10:1521/orcl      后面不要加: sqlplus  sys/oracle  as sysdb ...

  4. 案例47-crm练习登录校验拦截器

    1 LoginInterceptor package www.test.web.interceptor; import java.util.Map; import com.opensymphony.x ...

  5. nginx反向代理使用网址速度变慢

    最近公司网址加载静态文件的速度总是跟不上于是试着用带端口的ip来访问, 发现速度快不少于是将nginx的代理修改为ip的如: location / { proxy_pass http://localh ...

  6. 给string添加新的函数

    var str = "abcdefg";String.prototype.constr = function(){ return this.split('').join('-'); ...

  7. Javaweb三大组件-过滤器、监听器

    1. 过滤器 [filter] 作用: 对单个获取多个servlet起到增强[advice]的作用. 用于在所有的servlet执行前,做一些预处理.例如:做编码处理, 访问量统计[servletCo ...

  8. ThenJS

    安装ThenJs:  npm i thenjs 史上最快,与 node callback 完美结合的异步流程控制库 <!doctype html> <html lang=" ...

  9. 对SNMP4J的一些封装

    SNMP4J是一个开源的,用Java实现的snmp协议.其中提供了一下API,在这些API上面封装了一些方法,比如SNMP的get-request请求,get-next-request请求等 如果不了 ...

  10. scss-@each指令

    一.@each指令实例 在@each变量的定义,其中包含的每个项目的列表中的值. 语法: @each $var in <list or map> 语法简要说明如下. $var: 它代表了变 ...