【贪心】【动态规划】【数学思维】codeforces1152D Neko and Aki's Prank

D. Neko and Aki’s Prank
time limit per test1 second
memory limit per test256 megabytes
inputstandard input
outputstandard output
Neko is playing with his toys on the backyard of Aki’s house. Aki decided to play a prank on him, by secretly putting catnip into Neko’s toys. Unfortunately, he went overboard and put an entire bag of catnip into the toys…

It took Neko an entire day to turn back to normal. Neko reported to Aki that he saw a lot of weird things, including a trie of all correct bracket sequences of length 2n.

The definition of correct bracket sequence is as follows:

The empty sequence is a correct bracket sequence,
If s is a correct bracket sequence, then (s) is a correct bracket sequence,
If s and t are a correct bracket sequence, then st is also a correct bracket sequence.
For example, the strings “(())”, “()()” form a correct bracket sequence, while “)(” and “((” not.

Aki then came up with an interesting problem: What is the size of the maximum matching (the largest set of edges such that there are no two edges with a common vertex) in this trie? Since the answer can be quite large, print it modulo 109+7.

Input
The only line contains a single integer n (1≤n≤1000).

Output
Print exactly one integer — the size of the maximum matching in the trie. Since the answer can be quite large, print it modulo 109+7.

outputstandard output
Neko is playing with his toys on the backyard of Aki’s house. Aki decided to play a prank on him, by secretly putting catnip into Neko’s toys. Unfortunately, he went overboard and put an entire bag of catnip into the toys…

It took Neko an entire day to turn back to normal. Neko reported to Aki that he saw a lot of weird things, including a trie of all correct bracket sequences of length 2n.

The definition of correct bracket sequence is as follows:

The empty sequence is a correct bracket sequence,
If s is a correct bracket sequence, then (s) is a correct bracket sequence,
If s and t are a correct bracket sequence, then st is also a correct bracket sequence.
For example, the strings “(())”, “()()” form a correct bracket sequence, while “)(” and “((” not.

Aki then came up with an interesting problem: What is the size of the maximum matching (the largest set of edges such that there are no two edges with a common vertex) in this trie? Since the answer can be quite large, print it modulo 109+7.

Input
The only line contains a single integer n (1≤n≤1000).

Output
Print exactly one integer — the size of the maximum matching in the trie. Since the answer can be quite large, print it modulo 109+7.

Examples
inputCopy
1
outputCopy
1
inputCopy
2
outputCopy
3
inputCopy
3
outputCopy
9
Note
The pictures below illustrate tries in the first two examples (for clarity, the round brackets are replaced with angle brackets). The maximum matching is highlighted with blue.

在这里插入图片描述
在这里插入图片描述


  我们先来考虑一下,任意树中的最大匹配问题。主要会有两种解法,第一种是比较容易接受的dp法,状态就是 d p [ i ] [ 0 / 1 ] dp[i][0/1] dp[i][0/1]表示到第i个点,是否加入和儿子的匹配时,子树中的最大匹配数,递推方程很容易出来,可以参考 https://blog.csdn.net/lycheng1215/article/details/78368002
 我们可以看到,这个Trie树虽然很大,很多子树都是同一类型的,比如第i个符号之后,之前符号差为j时,子树的样子就是一样的。我于是一开始想,用 d p [ i ] [ j ] [ 0 / 1 ] dp[i][j][0/1] dp[i][j][0/1]表示在前i个符号中,左括号和右括号之差为j时,根节点是否参与儿子匹配时该类型子树的最大匹配数的和。方程也很简单。但问题来了,答案很大,求的过程中要不断取模。dp的时候就没法子取max了。
 事实上,对于一般的树,最大匹配可以有贪心解法。不断寻找叶子节点,删去它和它的父亲产生的点对即可。这么求很容易感知到是正确的,证明如下(来自于QZZ):

设原树最大匹配为n,去掉一个叶子和父亲后的新树最大匹配为m
即需证:m=n-1
设叶子为l,父亲为f
对于n的任意一种匹配方案,有两种可能情况:
1.f和l匹配,此时n1=m+1
2.l无匹配,f和相邻点x匹配,此时n2=k+1,k为去掉l点、f点和x点的树的最大匹配。这个树是新树的子集,所以k<=m,即n2<=m+1
n=max(n1,n2)=m+1

 现在问题就比较显然了,这个Trie是一个二叉树,而且叶子的深度都相同,那么很容易推出来实际要求的是树上的偶数层节点之和就是要求的答案。节点个数当然就很容易求了,直接dp来搞,注意把那些不可能的序列排除掉就行了。

#include<cstdio>
#define mo 1000000007
using namespace std;
using LL=long long;

int dp[2005][1005],n,cnt[2005],ans;

int main()
{
	scanf("%d",&n);
	dp[0][0]=1;
	for(int i=1;i<=2*n;i++)
		for(int j=0;j<=n;j++)
		{
			dp[i][j]=(dp[i-1][j+1]+(j>0?dp[i-1][j-1]:0))%mo;
			if(2*n-i>=j)
				cnt[i]=(cnt[i]+dp[i][j])%mo;
		}
	for(int i=1;i<=2*n;i+=2)
		ans=(ans+cnt[i])%mo;
	printf("%d",ans);
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值