2022下半年一些有趣(bushi)的题目记录

dp字符串匹配

Text Editor

链接:https://codeforces.com/contest/1701/problem/E
题意
你想写一个由字母组成的文本 T。但是,您已经编写了一个长度为N的小写字符串S,现在您希望通过从S中获取T来修复它。
最初,文本编辑器的光标位于文本 S的末尾(在最后一个字符之后)。在一个步骤中,您可以执行下列操作之一:
按下“左”按钮,光标就会向左移动一个位置(如果光标指向文本的开头,也就是第一个字符之前,光标就什么也不做) ;
按下“右”按钮,光标就会向右移动一个位置(如果光标指向文本的末尾,也就是在最后一个字符之后,光标就什么也不会做) ;
按下“主页”按钮,将光标移动到文本的开头(文本的第一个字符之前) ;
按“结束”按钮,将光标移动到文本的末尾(在文本的最后一个字符之后) ;
按下“退格”按钮,这样光标之前的字符将从文本中删除(如果光标之前没有字符,则不会发生任何操作)。
求最小操作数,如果无法获取就输出 -1。

const int N = 5e3 + 10, M = 1e6 + 5, MOD = 1e9 + 7, INF = 0x3f3f3f3f;
const double eps = 1e-8;	//一般比小数点的位数至少多 2 
const double pi = 3.141592653589;
const int P = 131;


int sum1[N];	// 前缀中b的第i个字母和a的第sum1[i]匹配
int dp1[N][N];
int dp2[N][N];

void solve()
{
	int n, m;
	cin >> n >> m;
	string a, b;
	cin >> a >> b;
	a = " " + a;
	b = " " + b;
	
	for (int i = 1, j = 1; i <= m; ++i, ++j)
	{
		while (j <= n && a[j] != b[i])
			j++;
		sum1[i] = j;
	}
	sum1[m + 1] = n + 1;
	
	if (sum1[m] > n)	// 不可能匹配上
	{
		cout << "-1" << endl;
		return ;
	}
	
	// 初始化
	for (int i = 0; i <= n + 1; ++i)
		for (int j = 0; j <= m + 1; ++j)
			dp1[i][j] = dp2[i][j] = 0;
	
	// 每一个长度为i的字符串变成长度为j的匹配字符串不需要移动的花费
	for (int i = 1; i <= n; ++i)
		for (int j = 1; j <= m; ++j)
			if (a[i] == b[j])
				dp1[i][j] = dp1[i - 1][j - 1] + 1;
	for (int i = n; i >= 1; --i)
		for (int j = m; j >= 1; --j)
			if (a[i] == b[j])
				dp2[i][j] = dp2[i + 1][j + 1] + 1;
			
	int ans = 2 * n;
	for (int fjd = 0; fjd <= m; ++fjd)	// 枚举T字符串的前后分界点
		for (int i = sum1[fjd]; i <= sum1[fjd + 1] - 1; ++i)
		{
			// 向右移动需要i步,i-fjd为要删除的字母数
			// 如果可以通过前面状态继承,则不需要删除,所以要减去dp1[i][fjd]
			int spend1 = i - fjd + i - dp1[i][fjd];
			
			// 向左移动需要n-i步,同时删除字母操作相当于向前移动了,所以不需要增加操作数目
			// 继承,减去dp2[i+1][fjd+1]
			int spend2 = n - i - dp2[i + 1][fjd + 1];
			
			ans = min(ans, spend1 + spend2 + (spend1 > 0));	// 只有大于0时需要跳转
		}
	cout << ans << endl;
	return ;
}

signed main()
{
	IOS;
	
	int T;
	cin >> T;
	while (T--)
	{
		solve();
	}
	
    return 0;
}

组合数

D. Madoka and The Corruption Scheme

链接:https://codeforces.com/contest/1717/problem/D
题意:有2^n个人,两两之间按照位置顺序比赛(这个位置顺序是可以自己随意排的),也可以自己决定每场是左边还是右边赢,决定完之后,赞助商有k次修改机会(如果原来决定左边赢,修改后则变成右边赢)。
思路:每场比赛都决定左边赢,如果赞助商要修改,唯有修改黑色边<=k的才能让该数字编号赢。
假如赞助商可以修改k次,那么列举,修改0次能赢的有C(n, 0),修改1次能赢的有C(n, 1),……,修改k次能赢的有C(n, k)。
由二项式可得C(n, 0) + C(n, 1) + …… + C(n, n) = 2^n。
将树和二进制对应,就是一个n位二进制,0~2^(n-1)和每个路径一一对应,黑边对应其中的1,在这些数中,包含恰好i个1的有C(n, i)个。

const int N = 2e5 + 10, M = 2e6 + 5, INF = 0x3f3f3f3f;
const double eps = 1e-8;	//一般比小数点的位数至少多 2 
const double pi = 3.141592653589;
const LL MOD = 1e9 + 7;

int fact[N + 10], infact[N + 10];

void F(int n, int p)
{
	fact[0] = infact[0] = 1;
	for (int i = 1; i <= n; ++i)
	{
		fact[i] = (LL)fact[i - 1] * i % p;
		infact[i] = (LL)infact[i - 1] * fastpow(i, p - 2, p) % p;
	}
}

int C(int a, int b, int p)
{
	if (a < b)
		return 0;
	return (LL)fact[a] * infact[a - b] % p * infact[b] % p;
}

void solve()
{
	LL n, k;
	cin >> n >> k;
	F(n, MOD);
	if (k >= n)
		cout << fastpow(2, n, MOD) << endl;
	else
	{
		LL ans = 0;
		for (int i = 0; i <= k; ++i)
			ans = (ans + C(n, i, MOD)) % MOD;
		cout << ans << endl;
	}
}

signed main()
{
	IOS;
	
	/*int T;
	cin >> T;
	while (T--)*/
		solve();
	
	Waku waku;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值