NOI2014 动物园

KMP补充:

  next【i】表示 1--i 的最长后缀等于前缀

  next【next【i】】 表示 1--i 的次长后缀等于前缀

题解:每个i 枚举所有next【i】 next【next【i】】。。。。。求小于长度 i/2 的个数

强行枚举next  50分


因为next【i】一定小于 i

所以建next【i】为 i 的爸爸 ,形成一个有根树,求每个节点的祖先有多少个小于i/2

我用栈加二分做        O(N *log N)

#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
int n;
//int num[1000001];
int next[2000001];
struct node
{
	int next;
	int to;
};
node bian[2000001];
int first[2000001];
int tt;
int stack[2000001];
char c[2000001];
int tail,size;
long long ans;

void inser(int a,int b)
{
	size++;
	bian[size].next=first[a];
	bian[size].to=b;
	first[a]=size;
}

void dfs(int x)
{
	int l,r,mid;
	if(x!=0)
	{
	l=0;
 	r=tail+1;
	while(r-l>1)
	  {
	  	mid=(r+l)>>1;
	  	if(stack[mid]<=(x>>1)) l=mid;
	  	else r=mid;
	  }
//	num[x]=r;
    ans=(ans*l)%1000000007;	
    }
	stack[++tail]=x;
	
	for(int i=first[x];i;i=bian[i].next)
	  {
	  	int u=bian[i].to;
	  	dfs(u);
	  }
    tail--;	  
}


int main()
{
	int _q=5*(10<<20);   
     char *_p=(char*)malloc(_q)+_q;
    __asm__("movl %0, %%esp\n"::"r"(_p));
	
	//freopen("zoo.in","r",stdin);
//	freopen("zoo.out","w",stdout);
	int i,j,k;
	scanf("%d\n",&tt);
	
	while(tt)
	{
		tt--;
//		memset(num,0,sizeof(num));
		memset(first,0,sizeof(first));
		size=0;
		next[0]=-1;
		gets(c+1);
		n=strlen(c+1);
		for(i=1;i<n;i++)
		  {
		  	 for(j=next[i];j!=-1;j=next[j])
		  	   if(c[i+1]==c[j+1]) break;
		  	 next[i+1]=j+1;   
		  }
		
		for(i=1;i<=n;i++)
		  inser(next[i],i);
		ans=1;  
		tail=0;
//	    tail=1;
//		stack[1]=0;
		dfs(0);
		cout<<ans;
		if(tt) cout<<endl;
	}
	
	
	
	return 0;
}


P2375 [NOI2014] 动物园是一道经典的动态规划题目,以下是该题的详细题意和解题思路。 【题意描述】 有两个长度为 $n$ 的整数序列 $a$ 和 $b$,你需要从这两个序列中各选出一些数,使得这些数构成一个新的序列 $c$。其中,$c$ 序列中的元素必须在原序列中严格递增。每个元素都有一个价值,你的任务是选出的元素的总价值最大。 【解题思路】 这是一道经典的动态规划题目,可以采用记忆化搜索的方法解决,也可以采用递推的方法解决。 记忆化搜索的代码如下: ```c++ #include <iostream> #include <cstdio> #include <cstring> using namespace std; const int MAXN = 1005; int dp[MAXN][MAXN], a[MAXN], b[MAXN], n; int dfs(int x, int y) { if (dp[x][y] != -1) return dp[x][y]; if (x == n || y == n) return 0; int res = max(dfs(x + 1, y), dfs(x + 1, y + 1)); if (a[x] > b[y]) { res = max(res, dfs(x, y + 1) + b[y]); } return dp[x][y] = res; } int main() { scanf("%d", &n); for (int i = 0; i < n; i++) scanf("%d", &a[i]); for (int i = 0; i < n; i++) scanf("%d", &b[i]); memset(dp, -1, sizeof(dp)); printf("%d\n", dfs(0, 0)); return 0; } ``` 其中,dp[i][j]表示选到a数组中第i个元素和b数组中第j个元素时的最大价值,-1表示未计算过。dfs(x,y)表示选到a数组中第x个元素和b数组中第y个元素时的最大价值,如果dp[x][y]已经计算过,则直接返回dp[x][y]的值。如果x==n或者y==n,表示已经遍历完一个数组,直接返回0。然后就是状态转移方程了,如果a[x] > b[y],则可以尝试选b[y],递归调用dfs(x, y+1)计算以后的最大价值。否则,只能继续遍历数组a,递归调用dfs(x+1, y)计算最大价值。最后,返回dp[0][0]的值即可。 递推的代码如下: ```c++ #include <iostream> #include <cstdio> #include <cstring> using namespace std; const int MAXN = 1005; int dp[MAXN][MAXN], a[MAXN], b[MAXN], n; int main() { scanf("%d", &n); for (int i = 0; i < n; i++) scanf("%d", &a[i]); for (int i = 0; i < n; i++) scanf("%d", &b[i]); for (int i = n - 1; i >= 0; i--) { for (int j = n - 1; j >= 0; j--) { dp[i][j] = max(dp[i + 1][j], dp[i + 1][j + 1]); if (a[i] > b[j]) { dp[i][j] = max(dp[i][j], dp[i][j + 1] + b[j]); } } } printf("%d\n", dp[0][0]); return 0; } ``` 其中,dp[i][j]表示选到a数组中第i个元素和b数组中第j个元素时的最大价值。从后往前遍历数组a和数组b,依次计算dp[i][j]的值。状态转移方程和记忆化搜索的方法是一样的。 【参考链接】 P2375 [NOI2014] 动物园:https://www.luogu.com.cn/problem/P2375
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值