zoukankan      html  css  js  c++  java
  • 郑州集训day1自闭有感

    被拉到郑州培训了

    考了一上午莫名自闭

    帮助慎老师拿到(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
    ",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
    ",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),表示存印器刚开始被存入了这个权值

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

  • 相关阅读:
    Serialization and deserialization are bottlenecks in parallel and distributed computing, especially in machine learning applications with large objects and large quantities of data.
    Introduction to the Standard Directory Layout
    import 原理 及 导入 自定义、第三方 包
    403 'Forbidden'
    https://bitbucket.org/ariya/phantomjs/downloads/phantomjs-2.1.1-linux-x86_64.tar.bz2
    These interactions can be expressed as complicated, large scale graphs. Mining data requires a distributed data processing engine
    mysqldump --flush-logs
    mysql dump 参数
    mysql dump 参数
    如果是在有master上开启了该参数,记得在slave端也要开启这个参数(salve需要stop后再重新start),否则在master上创建函数会导致replaction中断。
  • 原文地址:https://www.cnblogs.com/asuldb/p/10324488.html
Copyright © 2011-2022 走看看