JZOJ1421. 二叉树

题目

Description

在一个无穷的满二叉树中,有以下几个特点:
  (1) 每个节点都有两个儿子——左儿子和右儿子;
  (2) 如果一个节点的编号为X,则它的左儿子编号为2X,右儿子为2X+1;
  (3) 根节点编号为1。
  现在从根结点开始走,每一步有三种选择:走到左儿子、走到右儿子和停在原地。
  用字母“L”表示走到左儿子,“R”表示走到右儿子,“P”表示停在原地,用这三个字母组成的字符串表示一个明确的行走路线。
一个明确的行走路线的价值为最终到达节点的编号,例如LR的价值为5,而RPP的价值为3。
  我们用字符“L”、“R”、“P”和“”组成的字符串表示一组行走路线,其中“”可以是“L”、“R”、“P”中的任意一种,所有跟这个行走路线匹配的字符串都认为是可行的。
  例如L*R包含LLR、LRR和LPR。而**包含LL、LR、LP、RL、RR、RP、PL、PR和PP这9种路线。
  一组行走路线的价值等于所有匹配该模式的路线的价值之和。请你编程计算给定路线的价值。

Input

输入一个字符串表示一组行走路线,里面只含有“L”、“R”、“P”和“*”四种字符,长度不会超过10000。

Output

输出该路线的价值。

Sample Input

输入1:

P*P

输入2:

L*R

输入3:

**

输入4:

LLLLLRRRRRLLLLLRRRRRLLLLLRRRRRLLLLL

Sample Output

输出1:

6

输出2:

25

输出3:

33

输出4:

35400942560

Hint

【数据范围】
  30%的数据满足路线中不含“”;
  50%的数据满足最多只有3个“
”。

分析

对于前面50%的数据是非常简单的,
按照题目上面说的去做,加上一个高精度就没问题了。

如果要通过所以的数据,就要想一下其他办法。

题解

观察一下,几个符号分别有什么作用。

L:给目前的答案×2

R:给目前的答案×2,然后加上1。

P:给目前的答案×1,其实什么也没有干。

*:可以变成L,R,P,也就是对上面的求和。

如果全部是乘法,那么就是很简单的,可以用乘法分配律。
但是,现在还有加法。

我们对 * 操作来分析一下,

f i f_i fi 表示第i位的价值。

则可以得出以下结论:

L: f i f_i fi= f i − 1 f_{i-1} fi1×2

R: f i f_i fi= f i − 1 f_{i-1} fi1×2+1

P: f i f_i fi= f i − 1 f_{i-1} fi1×1

*: f i f_i fi=操作L+操作R+操作P= f i − 1 f_{i-1} fi1×5+1

  • 但是,后面的加1如何处理?

如果我们遇到了1个 * 操作,那么一个串就变成了3个串;

如果我们遇到了2个 * 操作,那么一个串就变成了9个串;

如果我们遇到了3个 * 操作,那么一个串就变成了27个串;

……

如果我们遇到了N个 * 操作,那么一个串就变成了 3 N 3^N 3N个串。

所以,每一个加1操作就是加上 3 N 3^N 3N

所以, f [ ] f[] f[] 的递推式就变为:

L: f i f_i fi= f i − 1 f_{i-1} fi1×2

R: f i f_i fi= f i − 1 f_{i-1} fi1×2+ 3 N 3^N 3N

P: f i f_i fi= f i − 1 f_{i-1} fi1×1

*: f i f_i fi= f i − 1 f_{i-1} fi1×5+ 3 N 3^N 3N

这里的N表示从1到i中,操作 * 的个数。

  • 注意高精度的时候可以压位。

code

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string.h>
#include <cmath>
#include <stdlib.h>
#include <math.h>
#define mo 10000
using namespace std;

typedef int arr [3000];

arr sum,num,ans,t;
char a[10003],ch;
int m,n;

void add(arr a,arr b)
{
	memset(t,0,sizeof(t));
	int m=max(a[0],b[0]);
	for(int i=1;i<=m;i++)
	{
		t[i]+=a[i]+b[i];
		t[i+1]+=t[i]/mo;
		t[i]-=t[i+1]*mo;
	}
	if(t[m+1]>0)t[0]=m+1;else t[0]=m;
}

void ttt(arr a,int b)
{
	memset(t,0,sizeof(t));
	for(int i=1;i<=a[0];i++)
	{
		t[i]+=a[i]*b;
		t[i+1]=t[i]/mo;
		t[i]-=t[i+1]*mo;
	}
	if(t[a[0]+1]>0)t[0]=a[0]+1;else t[0]=a[0];
}

int main()
{
	ch=getchar();
	while((ch!='P')&&(ch!='L')&&(ch!='R')&&(ch!='*'))ch=getchar();
	while((ch=='P')||(ch=='L')||(ch=='R')||(ch=='*'))
	{
		if(ch=='P')
		{
			ch=getchar();
			continue;
		}
		n++;
		a[n]=ch;
		ch=getchar();
	}
	sum[1]=1;
	sum[0]=1; 
	ans[1]=1;
	ans[0]=1; 
	
	for(int k=1;k<=n;k++)
	{
		if(a[k]=='L')
		{
			ttt(ans,2);
			memcpy(ans,t,sizeof(ans));
		}
		if(a[k]=='R')
		{
			ttt(ans,2);
			memcpy(ans,t,sizeof(ans));
			add(ans,sum);
			memcpy(ans,t,sizeof(t));
		}
		if(a[k]=='*')
		{
			ttt(ans,5);
			memcpy(ans,t,sizeof(ans));
			add(ans,sum);
			memcpy(ans,t,sizeof(t));
			ttt(sum,3);
			memcpy(sum,t,sizeof(sum));
		}
	}
	
	printf("%d",ans[ans[0]]);
	for(int i=ans[0]-1;i;i--)
	{
		if(ans[i]<1000)printf("0");
		if(ans[i]<100)printf("0");
		if(ans[i]<10)printf("0");
		printf("%d",ans[i]);
	}
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值