题目名称

改造二叉树

数字对

交换

英文名称

binary

pair

swap

输入文件名

binary.in

pair.in

swap.in

输出文件名

binary.out

pair.out

swap.out

时间限制

1s

2s

1s

空间限制

256M

256M

256M

测试点数目

20

20

10

测试点分值

5

5

10

是否有部分分

题目类型

传统

传统

传统

是否有SPJ

1.改造二叉树

【题目描述】

小Y在学树论时看到了有关二叉树的介绍:在计算机科学中,二叉树是每个结点最多有两个子结点的有序树。通常子结点被称作“左孩子”和“右孩子”。二叉树被用作二叉搜索树和二叉堆。随后他又和他人讨论起了二叉搜索树。

什么是二叉搜索树呢?二叉搜索树首先是一棵二叉树。设key[p]表示结点p上的数值。对于其中的每个结点p,若其存在左孩子lch,则key[p]>key[lch];若其存在右孩子rch,则key[p]<key[rch];注意,本题中的二叉搜索树应满足对于所有结点,其左子树中的key小于当前结点的key,其右子树中的key大于当前结点的key。

小Y与他人讨论的内容则是,现在给定一棵二叉树,可以任意修改结点的数值。修改一个结点的数值算作一次修改,且这个结点不能再被修改。若要将其变成一棵二叉搜索树,且任意时刻结点的数值必须是整数(可以是负整数或0),所要的最少修改次数。

相信这一定难不倒你!请帮助小Y解决这个问题吧。

【输入格式】

第一行一个正整数n表示二叉树结点数。结点从1~n进行编号。

第二行n个正整数用空格分隔开,第i个数ai表示结点i的原始数值。

此后n - 1行每行两个非负整数fa, ch,第i + 2行描述结点i + 1的父亲编号fa,以及父子关系ch,(ch = 0 表示i + 1为左儿子,ch = 1表示i + 1为右儿子)。

结点1一定是二叉树的根。

【输出格式】

仅一行包含一个整数,表示最少的修改次数。

【样例输入】

3

2 2 2

1 0

1 1

【样例输出】

2

【数据范围】

20 % :n <= 10 , ai <= 100.

40 % :n <= 100 , ai <= 200

60 % :n <= 2000 .

100 % :n <= 10 ^ 5 ,  ai < 2 ^ 31.

T1:

% :暴力。
% :可以用 DP 或者贪心或者神奇的暴力等其他奇怪的方法完成。
% :正解的 LIS 打成 O(n ^ )。
% :首先求出这颗二叉树的中序遍历,那么问题就转换成用最少的修改次数使这个整
数序列严格单调递增。于是很自然的想到了 LIS,但单纯用 LIS 是有一些问题的,
比如这种情况: , LIS 为 ,答案求出来为 ,但由于整数的限制,应该
要修改 次。即直接 LIS 求出的答案是在非严格递增的情况下的答案。
所以我们将原序列稍加修改, 一个常见的将严格递增整数序列映射成非严格递增整
数序列的技巧就是将如下序列:
a1, a2, a3, a4 ... an 映射成:
a1 - , a2 - , a3 - , a4 - ... an - n.
(这种方法常见于计数类问题)。
这样映射后求最长不下降子序列的长度就没问题了。
 #include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std; const int N = 1e5 + ;
int n, fa, d, sum, qr, l, r, mid, top, stk[N], f[N], a[N], b[N], lc[N], rc[N];
bool vis[N]; char ch;
int read() {
while (ch = getchar(), ch < '' || ch > '');
int res = ch - ;
while (ch = getchar(), ch >= '' && ch <= '') res = res * + ch - ;
return res;
} void Bfs() {
int x; stk[top = ] = ;
while (top) {
x = stk[top];
if (lc[x] && !vis[lc[x]]) {
stk[++top] = lc[x];
continue;
}
b[++sum] = a[x]; b[sum] -= sum;
vis[x] = true; --top;
if (rc[x] && !vis[rc[x]]) {
stk[++top] = rc[x];
continue;
}
}
return ;
} int main() {
freopen("binary.in", "r", stdin);
freopen("binary.out", "w", stdout);
n = read();
for (int i = ; i <= n; ++i) a[i] = read();
for (int i = ; i <= n; ++i) {
fa = read(); d = read();
(d ? rc[fa] : lc[fa]) = i;
}
Bfs();
f[qr = ] = b[];
for (int i = ; i <= n; ++i) {
if (b[i] >= f[qr]) f[++qr] = b[i];
else {
l = ; r = qr;
while (l <= r) {
mid = l + r >> ;
if (f[mid] <= b[i]) l = mid + ;
else r = mid - ;
}
f[l] = b[i];
}
}
cout << n - qr << endl;
fclose(stdin); fclose(stdout);
return ;
}

2.数字对

【题目描述】

小H是个善于思考的学生,现在她又在思考一个有关序列的问题。

她的面前浮现出一个长度为n的序列{ai},她想找出一段区间[L, R](1 <= L <= R <= n)。

这个特殊区间满足,存在一个k(L <= k <= R),并且对于任意的i(L <= i <= R),ai都能被ak整除。这样的一个特殊区间 [L, R]价值为R - L。

小H想知道序列中所有特殊区间的最大价值是多少,而有多少个这样的区间呢?这些区间又分别是哪些呢?你能帮助她吧。

【输入格式】

第一行,一个整数n.

第二行,n个整数,代表ai.

【输出格式】

第一行两个整数,num和val,表示价值最大的特殊区间的个数以及最大价值。

第二行num个整数,按升序输出每个价值最大的特殊区间的L.

【样例输入1】

5

4 6 9 3 6

【样例输出1】

1 3

2

【样例输入2】

5

2 3 5 7 11

【样例输出2】

5 0

1 2 3 4 5

【数据范围】

30%: 1 <= n <= 30 , 1 <= ai <= 32.

60%: 1 <= n <= 3000 , 1 <= ai <= 1024.

80%: 1 <= n <= 300000 , 1 <= ai <= 1048576.

100%: 1 <= n <= 500000 , 1 <= ai < 2 ^ 31.

T2:
% :暴力枚举判断。O(n^)。
% :特殊区间的特点实际上就是区间最小值等于这个区间的 GCD,于是暴力或递推算
出每个区间的最小值与 GCD。而对于最大价值,可以通过二分来进行求解。复杂
度 O(n ^ )。
%:在 %的基础上,最小值与 GCD 都使用 RMQ 算法来求解,对于这道题推荐使用
ST 表。最大价值仍然使用二分。复杂度 O(nlogn)。
 #include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std; const int N = 5e5 + , M = ;
int n, m, a, ans, l, r, mid, sum, A[N], f[N][M], g[N][M], p[M]; inline int Gcd(const int &x, const int &y) {
return y == ? x : Gcd(y, x % y);
} bool check(int len) {
int q = log2(len--), k = n + - p[q], j;
for (int i = ; i <= k; ++i) {
j = i + len;
if (min(f[i][q], f[j - p[q] + ][q]) == Gcd(g[i][q], g[j - p[q] + ][q]))
return true;
}
return false;
} char ch;
inline int read() {
while (ch = getchar(), ch < '' || ch > '');
int res = ch - ;
while (ch = getchar(), ch >= '' && ch <= '') res = res * + ch - ;
return res;
} char s[];
inline void print(int x) {
int res = ;
if (x == ) putchar('');
while (x) {
s[++res] = x % ;
x /= ;
}
for (int i = res; i; --i) putchar(s[i] + '');
putchar(' ');
return ;
} int main() {
freopen("pair.in", "r", stdin);
freopen("pair.out", "w", stdout);
n = read(); m = log2(n);
for (int i = ; i <= n; ++i) {
a = read();
f[i][] = g[i][] = a;
}
for (int i = ; i <= m; ++i) p[i] = << i;
for (int j = ; j <= m; ++j) {
int k = n + - p[j];
for (int i = ; i <= k; ++i) {
f[i][j] = min(f[i][j - ], f[i + p[j - ]][j - ]);
g[i][j] = Gcd(g[i][j - ], g[i + p[j - ]][j - ]);
}
}
l = ; r = n;
while (l <= r) {
mid = l + r >> ;
if (check(mid)) l = mid + ;
else r = mid - ;
}
ans = r;
if (ans == ) {
printf("%d %d\n", n, );
for (int i = ; i < n; ++i)
print(i);
printf("%d\n", n);
}
else {
int q = log2(ans--), k = n + - p[q], j;
for (int i = ; i <= k; ++i) {
j = i + ans;
if (min(f[i][q], f[j - p[q] + ][q]) == Gcd(g[i][q], g[j - p[q] + ][q]))
A[++sum] = i;
}
printf("%d %d\n", sum, ans);
for (int i = ; i < sum; ++i)
print(A[i]);
printf("%d\n", A[sum]);
}
fclose(stdin); fclose(stdout);
return ;
}

3.交换

【题目描述】

给定一个{0, 1, 2, 3, … , n - 1}的排列 p。一个{0, 1, 2 , … , n - 2}的排列q被认为是优美的排列,当且仅当q满足下列条件:

对排列s = {0, 1, 2, 3, ..., n - 1}进行n – 1次交换。

  1. 交换s[q0],s[q0 + 1]
  2. 交换s[q1],s[q1 + 1]

最后能使得排列s = p.

问有多少个优美的排列,答案对10^9+7取模。

【输入格式】

第一行一个正整数n.

第二行n个整数代表排列p.

【输出格式】

仅一行表示答案。

【样例输入】

3

1 2 0

【样例输出】

1

【样例解释】

q = {0,1} {0,1,2} ->{1,0,2} -> {1, 2, 0}

q = {1,0} {0,1,2} ->{0,2,1} -> {2, 0, 1}

【数据范围】

30%:  n <= 10

100%:  n <= 50

T3:
%:
枚举所有排列,判定即可。
%:
考虑倒着处理, 比如交换 (i, i + ), 那么前面的所有数不管怎么交换都无法到后面去(下
标恒小于等于 i),后面的数也是一样到不了前面。说明这最后一次交换前,就要求对于所有
的 x <= i, y > i,px<py。所以交换前左边的数是连续的,右边也是连续的。由于交换前, 前
面和后面的数是互相不干涉的, 所以就归结成了两个子问题。 于是我们可以用记忆化搜索来
解决这个问题。
设 dp[n][low] 代表长度为 n,H 是{low, low + ,…,low + n - }的排列,且 H 是 p 的子序
列,在 H 上优美序列的个数。
我们枚举交换哪两个相邻元素(k,k+), 然后判断 k 前面的所有数是否都小于后面的所有
数,如果是则进行转移 dp[n][low] += dp[k][low] * dp[n – k][low + k ] * C(n – , n – - k)。
即前面的 k 个元素与后面的 n - k 个元素是两个独立的子问题,前面是{low ... low + k - }的
排列,后面是{low + k ... low + n - }的排列,C(n - , n - - k)代表的是在交换(k, k + )前左
右两边还分别要进行 n - 次交换,而每次交换左边与交换右边是不同方案,这相当于 n -
个位置选择 n - - k 个位置填入,故还需要乘上 C(n - , n - - k)。时间复杂度为 O(n^)。
 #include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std; typedef long long ll;
const int N = , Mod = 1e9 + ;
int n, p[N], dp[N][N], C[N][N]; int Dfs(int len, int low) {
if (dp[len][low] != -) return dp[len][low];
if (len == ) return dp[len][low] = ;
int &res = dp[len][low]; res = ;
int t[N], m = , j, k;
for (int i = ; i <= n; ++i)
if (p[i] >= low && p[i] < low + len)
t[++m] = p[i];
for (int i = ; i < m; ++i) {
swap(t[i], t[i + ]);
for (j = ; j <= i; ++j)
if (t[j] >= low + i) break;
for (k = i + ; k <= m; ++k)
if (t[k] < low + i) break;
if (j > i && k > m) {
ll tmp = (ll)Dfs(i, low) * Dfs(m - i, low + i) % Mod;
tmp = tmp * C[m - ][i - ] % Mod;
res = (res + tmp) % Mod;
}
swap(t[i], t[i + ]);
}
return res;
} int main() {
freopen("swap.in", "r", stdin);
freopen("swap.out", "w", stdout);
scanf("%d", &n);
for (int i = ; i <= n; ++i) scanf("%d", &p[i]);
memset(dp, -, sizeof(dp));
for (int i = ; i <= n; ++i) {
C[i][] = ;
for (int j = ; j <= i; ++j)
C[i][j] = (C[i - ][j - ] + C[i - ][j]) % Mod;
}
Dfs(n, );
if (dp[n][] != -) cout << dp[n][] << endl;
else puts("");
fclose(stdin); fclose(stdout);
return ;
}

全国信息学奥林匹克联赛(NOIP2014)复赛 模拟题Day2 长乐一中的更多相关文章

  1. 全国信息学奥林匹克联赛 ( NOIP2014) 复赛 模拟题 Day1 长乐一中

    题目名称 正确答案  序列问题 长途旅行 英文名称 answer sequence travel 输入文件名 answer.in sequence.in travel.in 输出文件名 answer. ...

  2. 正确答案 全国信息学奥林匹克联赛( ( NOIP2014) 复 赛 模拟题 Day1 长乐一中

    [题目描述]小 H 与小 Y 刚刚参加完 UOIP 外卡组的初赛,就迫不及待的跑出考场对答案."吔,我的答案和你都不一样!",小 Y 说道,"我们去找神犇们问答案吧&qu ...

  3. 第二十四届全国青少年信息学奥林匹克联赛初赛 普及组C++语言试题

    第二十四届全国青少年信息学奥林匹克联赛初赛 普及组C++语言试题 1.原题呈现 2.试题答案 3.题目解析 因博客园无法打出公式等,所以给你们几个小编推荐的链接去看看,在这里小编深感抱歉! https ...

  4. CSP复赛day2模拟题

    没错,我又爆零了.....先让我自闭一分钟.....so 当你忘记努力的时候,现实会用一记响亮的耳光告诉你东西南北在哪. 好了,现在重归正题: 全国信息学奥林匹克联赛(NOIP2014) 复赛模拟题 ...

  5. 2016.11.14测试 长乐一中2014NOIP复赛模拟题 第一题。

    1.正确答案 [题目描述] 小H与小Y刚刚参加完UOIP外卡组的初赛,就迫不及待的跑出考场对答案. "吔,我的答案和你都不一样!",小Y说道,"我们去找神犇们问答案吧&q ...

  6. Qbxt 模拟题 day2(am) T2 jian

    [问题描述] 有N个数,随机选择一段区间,如果这段区间的所有数的平均值在[L,R]中则你比较厉害.求你比较厉害的概率. [输入格式] 第一行有三个数N, l, r,含义如上描述. 接下来一行有N个数代 ...

  7. cdoj 25 点球大战(penalty) 模拟题

    点球大战(penalty) Time Limit: 20 Sec  Memory Limit: 256 MB 题目连接 http://acm.uestc.edu.cn/#/problem/show/2 ...

  8. PMP全真模拟题真题試題含答案解析 2019年下半年PMP考試适用 PMP中文文对照试题 【香港台灣地區PMP考試也可用】

    PMP全真模拟题真题试题 含答案解析 2019年下半年PMP考试适用 PMP中文文对照试题 [香港台灣地區PMP考試也可用]PMP全真模擬題真題試題 含答案解析 2019年下半年PMP考試适用 PMP ...

  9. 更新 | 2019年9月计算机二级office模拟题库

    随着2019年上半年计算机二级考试的完美落幕,紧接着的便是9月份的考试了. 到目前为止,下半年9月份计算机二级考试报名开通时间在6月前后,现在也基本结束. 2019年9月(56次)全国计算机等级考试( ...

随机推荐

  1. highcharts的.net本地导出环境安装记录

    由于项目中highcharts需要内网使用,需要本地搭建导出的环境.下面简述下步骤: 1.下载开源的.net导出文件:https://github.com/imclem/Highcharts-expo ...

  2. dhtmlxScheduler日历日程控件包括天视图,周视图,月视图,年视图和日程表视图

    dhtmlxScheduler 是一个基于Web的类似于Outlook的日历日程控件. 它完全由javascript/js/css编写, 提供类似于MS Outlook Calendar, Apple ...

  3. Unity4.3.3激活

    Unity4.X Win版本的破解方法: <ignore_js_op> 1.安装unity4.X,一路按提示下一步,要断网,直到激活运行软件后再联网2.将Unity 4.x Pro Pat ...

  4. Linear Predictors

    In this chapter we will study the family of linear predictors, one of the most useful families of hy ...

  5. swift语言开发的一个游戏------熊猫跑酷(KongfuPanda)

    项目地址:https://github.com/jakciehoo/KongfuPanda 欢迎加QQ群:260558552.大家一起交流iOS开发,我们可以一起学习,我很想集结一些志同道合的朋友,一 ...

  6. cf--2A

    //Accepted 100 KB 92 ms //模拟 #include <cstdio> #include <cstring> #include <iostream& ...

  7. ulink 固件更新问题

    1前言 ulink版本升级.. 当使用keil4.7.4版本时,ulink固件库更新了. 2官方资料解释: Boot Mode Utility Home » Appendix » Utilities  ...

  8. MATLAB里的正则表达式 [转]

    正则表达式在处理字符串及文本时显得十分方便,在perl, python等脚本语言,以及java, .net等平台上都支援正则表达式.事实上,在MATLAB中也提供了正则表达式的支持.主要包含三个常用的 ...

  9. (转)phonegap 数据库详解

    原文:http://firepix.iteye.com/blog/1618343 phonegap 数据库详解          博客分类: web App phonegap  今天就把之前使用pho ...

  10. hdu 2061

    PS:  以为找个简单来恢复信心..结果碰到那么傻逼的题目... 题意:给出学分和成绩,算GPA...关键是注意换行....它要求的换行我觉得超级奇怪...除了第一个正常,其他的输入完之后先一个换行. ...