题目描述
一个二叉树,树中每个节点的权值互不相同。
现在给出它的后序遍历和中序遍历,请你输出它的层序遍历。
输入格式
第一行包含整数 N N N,表示二叉树的节点数。
第二行包含 N N N 个整数,表示二叉树的后序遍历。
第三行包含 N N N 个整数,表示二叉树的中序遍历。
输出格式
输出一行 N N N 个整数,表示二叉树的层序遍历。
数据范围
1 ≤ N ≤ 30 1≤N≤30 1≤N≤30
官方并未给出各节点权值的取值范围,为方便起见,在本网站范围取为 1 ∼ N 1∼N 1∼N。
输入样例
7
2 3 1 5 7 6 4
1 2 3 4 5 6 7
输出样例
4 1 6 3 5 7 2
具体实现
#include
using namespace std;
const int N=35;
int n;
//a[N]表示后序遍历
//b[N]表示中序遍历
//p[N]存储在中序遍历中每一个数值的位置
int a[N],b[N],p[N];
//每一层开一个vector用来记录每一层的层序遍历
vector<int>level[N];
//传入a数组的起点和终点,b数组的起点和终点,d表示层数
void build(int al,int ar,int bl,int br,int d)
{
if(al>ar)
{
return ;
}
int val=a[ar]; //val表示根节点
level[d].push_back(val);
int k=p[val]; //p[val]表示根节点在b数组中的位置
build(al,al+k-1-bl,bl,k-1,d+1); //左子树的中序遍历和后序遍历
build(al+k-bl,ar-1,k+1,br,d+1); //右子树的中序遍历和后序遍历
}
int main()
{
cin>>n;
for(int i=0;i<n;i++)
{
cin>>a[i];
}
for(int i=0;i<n;i++)
{
cin>>b[i];
}
for(int i=0;i<n;i++)
{
p[b[i]]=i;
}
build(0,n-1,0,n-1,0);
for(int i=0;i<n;i++)
{
for(int x: level[i])
{
cout<<x<<" ";
}
}
return 0;
}
题目描述
或许你并不知道,你的某个朋友是你的亲戚。
他可能是你的曾祖父的外公的女婿的外甥女的表姐的孙子。
如果能得到完整的家谱,判断两个人是否是亲戚应该是可行的,但如果两个人的最近公共祖先与他们相隔好几代,使得家谱十分庞大,那么检验亲戚关系实非人力所能及。
在这种情况下,最好的帮手就是计算机。
为了将问题简化,你将得到一些亲戚关系的信息,如 Marry 和 Tom 是亲戚,Tom 和 Ben 是亲戚,等等。
从这些信息中,你可以推出 Marry 和 Ben 是亲戚。
请写一个程序,对于我们的关于亲戚关系的提问,以最快的速度给出答案。
输入格式
输入由两部分组成。
第一部分以 N , M N,M N,M 开始。 N N N 为问题涉及的人的个数。这些人的编号为 1 , 2 , 3 , … , N 1,2,3,…,N 1,2,3,…,N。下面有 M M M 行,每行有两个数 a i , b i a_{i},b_{i} ai,bi,表示已知 a i a_{i} ai 和 b i b_{i} bi 是亲戚。
第二部分以 Q Q Q 开始。以下 Q Q Q 行有 Q Q Q 个询问,每行为 c i , d i c_{i},d_{i} ci,di,表示询问 c i c_{i} ci 和 d i d_{i} di 是否为亲戚。
输出格式
对于每个询问 c i , d i c_{i},d_{i} ci,di,输出一行:若 c i c_{i} ci 和 d i d_{i} di 为亲戚,则输出 Yes
,否则输出 No
。
数据范围
1 ≤ N ≤ 20000 1≤N≤20000 1≤N≤20000
1 ≤ M ≤ 1 0 6 1≤M≤10^{6} 1≤M≤106
1 ≤ Q ≤ 1 0 6 1≤Q≤10^{6} 1≤Q≤106
输入样例
10 7
2 4
5 7
1 3
8 9
1 2
5 6
2 3
3
3 4
7 10
8 9
输出样例
Yes
No
Yes
具体实现
#include
using namespace std;
const int N = 20010;
int n, m;
int p[N];
int find(int x)
{
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i ++ ) p[i] = i;
while (m -- )
{
int a, b;
scanf("%d%d", &a, &b);
p[find(a)] = find(b);
}
scanf("%d", &m);
while (m -- )
{
int a, b;
scanf("%d%d", &a, &b);
if (find(a) == find(b)) puts("Yes");
else puts("No");
}
return 0;
}
题目描述
奶牛贝茜正在学习如何在不同进制之间转换数字。
但是她总是犯错误,因为她无法轻易的用两个前蹄握住笔。
每当贝茜将数字转换为一个新的进制并写下结果时,她总是将其中的某一位数字写错。
例如,如果她将数字 14 14 14 转换为二进制数,那么正确的结果应为 1110 1110 1110,但她可能会写下 0110 0110 0110 或 1111 1111 1111。
贝茜不会额外添加或删除数字,但是可能会由于写错数字的原因,写下包含前导 0 0 0 的数字。
给定贝茜将数字 N N N 转换为二进制数字以及三进制数字的结果,请确定 N N N 的正确初始值(十进制表示)。
输入格式
第一行包含 N N N 的二进制表示,其中一位是错误的。
第二行包含 N N N 的三进制表示,其中一位是错误的。
输出格式
输出正确的 N N N 的值。
数据范围
0 ≤ N ≤ 1 0 9 0≤N≤10^{9} 0≤N≤109 ,且存在唯一解。
输入样例
1010
212
输出样例
14
样例解释
14 14 14 在二进制下的正确表示为 1110 1110 1110,在三进制下的正确表示为 112 112 112。
具体实现
#include
using namespace std;
int base(string s,int b) //将b进制的数转换成10进制
{
int res=0;
for(int i=0;i<s.size();i++)
{
res=res*b+s[i]-'0';
}
return res;
}
int main()
{
//x是二进制表示的结果
//y是三进制表示的结果
string x,y;
cin>>x>>y;
unordered_set<int>hash;
for(int i=0;i<x.size();i++)
{
string s=x;
s[i]^=1; //0,1互相转换
if(s.size()>1&&s[0]=='0')
{
continue;
}
hash.insert(base(s,2));
}
for(int i=0;i<y.size();i++)
{
for(int j=0;j<3;j++)
{
if(y[i]-'0'!=j)
{
string s=y;
s[i]=j+'0';// 0,1,2相互转换
if(s.size()>1&&s[0]=='0')
{
continue;
}
int n=base(s,3);
if(hash.count(n))
{
cout<<n<<endl;
}
}
}
}
return 0;
}
题目描述
给定一个长度为 N N N 的序列 A A A,要求把该序列分成若干段,在满足每段中所有数的和不超过 M M M 的前提下,让每段中所有数的最大值之和最小。
试计算这个最小值。
输入格式
第一行包含两个整数 N N N 和 M M M。
第二行包含 N N N 个整数,表示完整的序列 A A A。
输出格式
输出一个整数,表示结果。
如果结果不存在,则输出 − 1 −1 −1。
数据范围
0 ≤ N ≤ 1 0 5 0≤N≤10^{5} 0≤N≤105
0 ≤ M ≤ 1 0 11 0≤M≤10^{11} 0≤M≤1011
序列 A A A 中的数非负,且不超过 1 0 6 10^{6} 106。
输入样例
8 17
2 2 2 8 1 8 2 1
输出样例
12
具体实现
#include
#include
#include
#include
using namespace std;
typedef long long LL;
const int N = 100010;
int n;
LL m;
int w[N], q[N];
LL f[N];
multiset<LL> S;
void remove(LL x)
{
auto it = S.find(x);
S.erase(it);
}
int main()
{
scanf("%d%lld", &n, &m);
for (int i = 1; i <= n; i ++ )
{
scanf("%d", &w[i]);
if (w[i] > m)
{
puts("-1");
return 0;
}
}
int hh = 0, tt = -1;
LL sum = 0;
for (int i = 1, j = 1; i <= n; i ++ )
{
sum += w[i];
while (sum > m)
{
sum -= w[j ++ ];
if (hh <= tt && q[hh] < j)
{
if (hh < tt) remove(f[q[hh]] + w[q[hh + 1]]);
hh ++ ;
}
}
while (hh <= tt && w[q[tt]] <= w[i])
{
if (hh < tt) remove(f[q[tt - 1]] + w[q[tt]]);
tt -- ;
}
q[ ++ tt] = i;
if (hh < tt) S.insert(f[q[tt - 1]] + w[q[tt]]);
f[i] = f[j - 1] + w[q[hh]];
if (S.size()) f[i] = min(f[i], *S.begin());
}
printf("%lld\n", f[n]);
return 0;
}
题目描述
一个字符串的前缀是从第一个字符开始的连续若干个字符,例如 abaab
共有 5 5 5 个前缀,分别是 a
,ab
,aba
,abaa
,abaab
。
我们希望知道一个 N N N 位字符串 S S S 的前缀是否具有循环节。
换言之,对于每一个从头开始的长度为 i ( i > 1 ) i(i>1) i(i>1)的前缀,是否由重复出现的子串 A A A 组成,即 A A A … A AAA…A AAA…A ( A A A 重复出现 K K K 次, K > 1 K>1 K>1)。
如果存在,请找出最短的循环节对应的 K K K 值(也就是这个前缀串的所有可能重复节中,最大的 K K K 值)。
输入格式
输入包括多组测试数据,每组测试数据包括两行。
第一行输入字符串 S S S 的长度 N N N。
第二行输入字符串 S S S。
输入数据以只包括一个 0 0 0 的行作为结尾。
输出格式
对于每组测试数据,第一行输出 Test case #
和测试数据的编号。
接下来的每一行,输出具有循环节的前缀的长度 i i i 和其对应 K K K,中间用一个空格隔开。
前缀长度需要升序排列。
在每组测试数据的最后输出一个空行。
数据范围
2 ≤ N ≤ 1000000 2≤N≤1000000 2≤N≤1000000
输入样例
3
aaa
4
abcd
12
aabaabaabaab
0
输出样例
Test case #1
2 2
3 3
(空行)
Test case #2
(空行)
Test case #3
2 2
6 2
9 3
12 4
(空行)
具体实现
#include
using namespace std;
const int N = 1000010;
int n;
char s[N];
int ne[N];
int main()
{
int T=1;
while(scanf("%d",&n),n)
{
scanf("%s",s+1);
for(int i=2,j=0;i<=n;i++)
{
while(j&&s[i]!=s[j+1])
{
j=ne[j];
}
if(s[i]==s[j+1])
{
j++ ;
}
ne[i]=j;
}
printf("Test case #%d\n",T);
T++;
for(int i=1;i<=n;i++)
{
int t=i-ne[i];
if(i%t==0&&i/t>1)
{
printf("%d %d\n",i,i/t);
}
}
puts("");
}
return 0;
}