20161107的考试】树论基础,计数dp,字符串计数dp

20 篇文章 0 订阅
6 篇文章 0 订阅

……DP专场引发的血案【xxx

………………今天如果是NOIP那我就退役了【x

……虽然的确是noipd考试范围内【跪地不起


T1:

题意:有根树,点有标号,求有多少棵子树上的编号是连续的一段整数

思路:记录每棵子树的size,minlable和maxlable,算一下就好了

代码:

#include<bits/stdc++.h>
#define MAXN 100005
using namespace std;	int n;
vector<int> son[MAXN];
int ind[MAXN];
int cnt = 0,root;
int siz[MAXN];
int mn[MAXN],mx[MAXN];
void dfs(int now) {
	mx[now] = mn[now] = now;
	siz[now] = 1;
	for (int i=0;i<son[now].size();++i) {
		int aim = son[now][i];
		dfs(aim);
		siz[now] += siz[aim];
		if(mx[aim] - mn[aim] + 1 == siz[aim])	++cnt;
		mx[now] = max(mx[now],mx[aim]);
		mn[now] = min(mn[now],mn[aim]);
	}
}

int read_x,read_y;
int main() {
	freopen("A.in","r",stdin);
	freopen("A.out","w",stdout);
	
	scanf("%d",&n);
	for (int i=1;i<n;++i)
		scanf("%d%d",&read_x,&read_y),son[read_x].push_back(read_y),++ind[read_y];
	for (int i=1;i<=n;++i)
		if(!ind[i]) {
			root = i;
			break;
		}
	dfs(root);

	printf("%d",cnt+1);
	return 0;
}

T2

题意:对于一个1~n的排列,可以用两个数之间的大于小于关系来表示,给出这个表示,求有多少1~n的排列满足这个表示

思路:GG

         反正我先考虑的是数位dp一样的递推东西【大概是递推写多了的后遗症QAQ】……然后判去重啥的想了一年发现还是n^3的复杂度……GG

         最后……发现,转移和递推式基本一样【跪地不起】,区别就是初始化那儿……

         反正递推啥的就是用f[i][j]表示第i位填j的话有多少方案,然后枚举下一位填的合法的转移过去;如果没有限制每个数只出现一次显然这么搞。

         然而限制了次数的话,还是想f[i][j],但是是表示填了前i位,且第i位填的是填了的数中第j大的一个。……显然转移的时候无论如何都不会重…………嗯……完事了…………至于如何让转移变成……O(1)的……前缀和后缀和啥的瞎搞搞就好了

         于是感谢CYZ大爷又让我涨了姿势……这个前缀后缀和的写法我给满分【因为转移的时候系数都是1于是很显然了【

代码:

#include<bits/stdc++.h>
#define MOD 1000000007
#define MAXN 1010
using namespace std;	char s[MAXN];	int n;
int f[MAXN][MAXN];

int main(){
	freopen("B.in","r",stdin);
	freopen("B.out","w",stdout);
	
	scanf("%s",s);
	n = strlen(s);
	f[1][1] = 1;
	for(int i=0;i<n;++i){
		int x = i+2;
		if(s[i]=='I'){
			for(int j=2;j<=x;++j)	f[x][j] = f[x-1][j-1];
			for(int j=1;j<=x;++j)	f[x][j] = (f[x][j] + f[x][j-1]) %MOD; 
		}
		else	if(s[i]=='D'){
			for(int j=1;j<=x;++j)	f[x][j] = f[x-1][j];
			for(int j=x;j;--j)	f[x][j] = (f[x][j] + f[x][j+1])	%MOD;
		}
		else{
			int tmp = 0;
			for(int j=1;j<=x;++j)	(tmp += f[x-1][j]) %= MOD;
			for(int j=1;j<=x;++j)	f[x][j] = tmp;
		}
	}
	int ans = 0 ;
	++n;
	for(int i=1;i<=n;++i)	(ans += f[n][i]) %= MOD;
	printf("%d",ans);
	return 0;
}

T3

题意:两个字符串s1,s2,设它们的最长公共子序列的长度为L,求s1中长度为L的子序列有多少个在s2中出现过

思路:……想了想lcs怎么写啊估计要yy一会儿……先磕T2,GG

           先求lcs,f[i][j]表示s1中前i个和s2中前j个的lcs长度。

           …………我还是粘题解吧……反正……就这个意思23333

首先用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值