10.25 小记

今天乱做了几个 DP 题。

随便写一写。

ARC081C Don't be a subsequence

给你一个字符串,求最短的不是该字符串的子序列的字符串,如果有多个,求字典序最小的一个。


我们设 \(f_{i}\) 为对于从后往前第 \(i\) 个位置,生成一个不是子序列所需的长度,然后我们考虑在当前位置,然后现在想要加一个字符,这个字符会匹配到后面的字符,而且新加一个字符之后一定会匹配到在它右面的最近的一个相同的字符。设 \(nxt_{i,j}\) 为在 \(i\) 之后第一个出现 \(j\) 的位置,那么从 \(i\)\(nxt_{i,j}\) 之间是只有一个 \(j\) ,就说明在 \(f_{nxt_{i,j}+1}\) 后插入一个 \(j\) 一定不会让不是子序列的东西变成子序列。

这样 DP 方程就出现了 \(f_{i}=\min f_{nxt_{i,j}+1}+1\)

\(f_{n+1} =1\) 因为这时候需要先随便放上一个字符。

然后需要求方案数,那么我们需要从前向后枚举字母,然后存在一个字母 \(j\),使得 \(f_{i}=f_{nxt_{i,j}+1}+1\),就说明这个 \(j\) 是答案,所以把 \(i\) 跳到 \(nxt_{i,j}+1\) 的位置上,这样跳 \(f_1\) 次就是答案。

[CEOI2017] Chase

首先有一个 \(O(n^2)\) 的想法,假设每个点是起点,然后所有点都从起点走,所以把当前点当成根。设 \(f_{i,j,0/1}\) 表示到节点 \(i\),已经使用了 \(j\) 块磁铁,当前点放/不放磁铁的追逐者减逃亡者的最大差值。

然后对于 \(f_{i,j,0}=\max \{\max\{f_{v,j,0},f_{v,j,1}\}\},f_{i,j,1}=\max\{\max\{f_{v,j-1,0}+f_{v,j-1,1}\}+sum_i\}\)

\(sum_i\) 表示 $i $ 的所有儿子的和。

然后就是考虑换根 DP,换根 DP 都是将父亲的贡献部分减掉当前点的贡献再贡献回自己。但是由于是取最值所以没办法刨除贡献,所以按照套路,我们维护一个最大值和一个次大值,然后看是否与最大值相等即可。

然后有一个显著但是我不会的减少码量的办法就是在算完 \(f_{p}\) 处理子树的时候把 \(f_p\) 直接改成能转移到 \(f_v\) 的那种形式,这样方便后面求最大值和次大值。

细节还是挺多的。

[清华集训2016] 组合数问题

真的是奇妙数论+数位 DP 的奇妙组合!

如果想要满足是 \(k\) 的倍数,就说明这个数除以 \(k\) 的余数为 \(0\),然后就是奇妙的转化。

根据卢卡斯定理,\(\dbinom{n}{m}=\dbinom{n\mod k}{m\mod k}\times \dbinom{\lfloor\frac{n}{k}\rfloor}{\lfloor\frac{m}{k}\rfloor}(\mod k)\)

然后可以观察发现,\(\lfloor \frac{n}{k}\rfloor \geq \lfloor \frac{m}{k}\rfloor\)

所以如果能让这个数等于 \(0\),那一定是 \(n\mod k< m\mod k\)

然后我们可以想到的是,将 \(n,m\) 转化为 \(k\) 进制的数字,我们需要求的就是对于当前位来说,\(n\) 对应的数字比 \(m\) 对应的数字小的数有多少个?那么我们就可以数位 DP 做了。

然后我为了方便,改成了求当前位 \(n\) 对应的数字大于等于 \(m\) 对应的数字的个数,然后用 \(\frac {(m+1)\times (m+2) }{2}+(n-m)\times (m+1)\) 减就行了。

ll dfs(int p,int lim1,int lim2)
{
	if(!p) return 1;
	if(f[p][lim1][lim2]!=-1) return f[p][lim1][lim2];
	int k1=lim1?cnt1[p]:k-1;
	int k2=lim2?cnt2[p]:k-1;
	ll ans=0;
	for(int i=0;i<=k1;i++)
	{
		for(int j=0;j<=min(k2,i);j++)
			ans=(ans+dfs(p-1,lim1&&i==k1,lim2&&j==k2))%mod;
	}	
	return f[p][lim1][lim2]=ans;
}
ll calc(ll n,ll m)
{
	m=min(n,m);
	memset(f,-1,sizeof(f));
	x1=0,x2=0;
	memset(cnt1,0,sizeof(cnt1));
	memset(cnt2,0,sizeof(cnt2));
	ll nn=n,mm=m;
	while(nn)
		cnt1[++x1]=nn%k,nn/=k;
	while(mm)
		cnt2[++x2]=mm%k,mm/=k;
	ll ret=dfs(x1,1,1);
	ll ans=(((m+1)%mod*((m+2)%mod))%mod*500000004%mod+(n-m)%mod*((m+1)%mod)%mod)%mod;
	return ((ans-ret)%mod+mod)%mod;
}

[BalticOI 2009 Day1]甲虫

我们设 \(f_{i,j,0/1}\) 表示从 \(i\)\(j\) 的水滴中,被太阳损耗了的水分,且此时甲虫在左端/右端。

但是这样转移方程里不包含时间,也就是说可能存在某一时刻虽然满足条件,但可能存在某些水滴被晒成了负数,显然不合法。

那么我们需要枚举当前能喝到的水滴数 \(l\),显然,能喝到的水滴数为连续的。

那么状态变成了 \(f_{i,j,0/1}\) 把 $i\sim j $ 范围内所有水都喝掉所浪费掉的最少水分,此时甲虫在左边/右边。

然后转移方程就不难了。

	for(int k=1;k<=l;k++)
	{
		for(int i=1;i<=n-k+1;i++)
		{
			int j=i+k-1;
			f[i-1][j][0]=min({f[i-1][j][0],f[i][j][0]+(l-k)*(a[i]-a[i-1]),f[i][j][1]+(l-k)*(a[j]-a[i-1])});
			f[i][j+1][1]=min({f[i][j+1][1],f[i][j][1]+(l-k)*(a[j+1]-a[j]),f[i][j][0]+(l-k)*(a[j+1]-a[i])});
		}
	}

\(l\) 代表此时枚举的喝掉的水滴数。

[AGC002F] Leftmost Ball

这是一场模拟赛的题,距离今天已经整整一年了,但我仍然不会呜呜呜。

就是我们观察到每个位置白球的前缀数量一定大于等于其他颜色球的数量,所以设 \(f_{i,j}\) 表示已经在序列中放入了 \(i\) 个白球,\(j\) 个不同颜色的球。\(i\geq j\),这样保证了合法性。

那么现在考虑插入球。考虑现在插入的是白球,那么要把这个白球放在最前面的空位上,所以此时 \(f_{i,j}+=f_{i-1,j}\)

如果现在插入彩球,考虑将先将第一个插入最靠左的空位,然后剩下的球在剩下的空位上随便挑。

\(f_{i,j}=f_{i-1,j}+f_{i,j-1}\times (n-j+1)\times \dbinom{k-2}{n\times k-i-(j-1)\times(k-1)-1}\)

废话

4ffafdc61088a806eb36261afe7793f6.jpg

如果说所有悲欢都将在喧嚣中淹没

总有人与我不期而遇在迷茫的路口

为我寻回遗失现实角落的梦

为世界带来久违的温柔

风的欢笑,雨的哭声

融化裹挟谁平凡的感动

身后闪烁万家灯火

将人间的故事诉说给星空

无论春夏,无论秋冬

无论多少岁月将我们分割

摘下耳机眼眶依旧会微红

戴上耳机依旧是你描绘的梦

posted @ 2022-10-25 19:33  cc0000  阅读(60)  评论(1)    收藏  举报