全国信息学奥林匹克联赛(NOIP2014)复赛 模拟题Day2 长乐一中
题目名称 |
改造二叉树 |
数字对 |
交换 |
英文名称 |
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次交换。
- 交换s[q0],s[q0 + 1]
- 交换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 长乐一中的更多相关文章
- 全国信息学奥林匹克联赛 ( NOIP2014) 复赛 模拟题 Day1 长乐一中
题目名称 正确答案 序列问题 长途旅行 英文名称 answer sequence travel 输入文件名 answer.in sequence.in travel.in 输出文件名 answer. ...
- 正确答案 全国信息学奥林匹克联赛( ( NOIP2014) 复 赛 模拟题 Day1 长乐一中
[题目描述]小 H 与小 Y 刚刚参加完 UOIP 外卡组的初赛,就迫不及待的跑出考场对答案."吔,我的答案和你都不一样!",小 Y 说道,"我们去找神犇们问答案吧&qu ...
- 第二十四届全国青少年信息学奥林匹克联赛初赛 普及组C++语言试题
第二十四届全国青少年信息学奥林匹克联赛初赛 普及组C++语言试题 1.原题呈现 2.试题答案 3.题目解析 因博客园无法打出公式等,所以给你们几个小编推荐的链接去看看,在这里小编深感抱歉! https ...
- CSP复赛day2模拟题
没错,我又爆零了.....先让我自闭一分钟.....so 当你忘记努力的时候,现实会用一记响亮的耳光告诉你东西南北在哪. 好了,现在重归正题: 全国信息学奥林匹克联赛(NOIP2014) 复赛模拟题 ...
- 2016.11.14测试 长乐一中2014NOIP复赛模拟题 第一题。
1.正确答案 [题目描述] 小H与小Y刚刚参加完UOIP外卡组的初赛,就迫不及待的跑出考场对答案. "吔,我的答案和你都不一样!",小Y说道,"我们去找神犇们问答案吧&q ...
- Qbxt 模拟题 day2(am) T2 jian
[问题描述] 有N个数,随机选择一段区间,如果这段区间的所有数的平均值在[L,R]中则你比较厉害.求你比较厉害的概率. [输入格式] 第一行有三个数N, l, r,含义如上描述. 接下来一行有N个数代 ...
- cdoj 25 点球大战(penalty) 模拟题
点球大战(penalty) Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://acm.uestc.edu.cn/#/problem/show/2 ...
- PMP全真模拟题真题試題含答案解析 2019年下半年PMP考試适用 PMP中文文对照试题 【香港台灣地區PMP考試也可用】
PMP全真模拟题真题试题 含答案解析 2019年下半年PMP考试适用 PMP中文文对照试题 [香港台灣地區PMP考試也可用]PMP全真模擬題真題試題 含答案解析 2019年下半年PMP考試适用 PMP ...
- 更新 | 2019年9月计算机二级office模拟题库
随着2019年上半年计算机二级考试的完美落幕,紧接着的便是9月份的考试了. 到目前为止,下半年9月份计算机二级考试报名开通时间在6月前后,现在也基本结束. 2019年9月(56次)全国计算机等级考试( ...
随机推荐
- hihocoder 1236(2015北京网络赛 J题) 分块bitset乱搞题
题目大意: 每个人有五门课成绩,初始给定一部分学生的成绩,然后每次询问给出一个学生的成绩,希望知道在给定的一堆学生的成绩比这个学生每门都低或者相等的人数 因为强行要求在线查询,所以题目要求,每次当前给 ...
- bzoj 2440 简单莫比乌斯反演
题目大意: 找第k个非平方数,平方数定义为一个数存在一个因子可以用某个数的平方来表示 这里首先需要考虑到二分才可以接下来做 二分去查找[1 , x]区间内非平方数的个数,后面就是简单的莫比乌斯反演了 ...
- GITHUB的初次使用
对于一个从未用过 接触过github的人来说,达到一个最终的成功真可谓是历经千辛万苦.在这里真的感谢我们的小组组长,我遇到的问题除了自己的查到的,剩 下的基本上都是组长帮我解决的.当所有 ...
- Section 1.4 Mother's Milk
又是一道怨念已久的题目0 0之前深搜写过广搜写过,怎么就是卡死,我还以为FP坏了重新装了一遍.今天偶尔翻起来,发现广搜忘记inc(head)了…简直哭瞎… 简单的广搜,分类比较多,不过不太要动脑子.至 ...
- Spring学习笔记之整合hibernate
1.web.xml里边要配置好对应的springxml的路径 <context-param> <param-name>contextConfigLocation</par ...
- android selector详解
--> 改变字体的颜色<selector xmlns:android="http://schemas.android.com/apk/res/android"> ...
- DirectX 总结和DirectX 9.0 学习笔记
转自:http://www.cnblogs.com/graphics/archive/2009/11/25/1583682.html DirectX 总结 DDS DirectXDraw Surfac ...
- 12-27cell常用的属性
1.创建cell // 创建一个cell并且设置cell的风格 UITableViewCell *cell = [[UITableViewCell alloc]initWithStyle:UI ...
- WebGrid Enterprise免费下载
WebGrid.NET Enterprise是一个为ASP.NET平台下WEB开发而设计的高级数据表格控件.WebGrid.NET为复杂的分层次导航交互式企业级信息传输提供了全面而先进的功能,它允许用 ...
- ubuntu添加共享出错
早上设置一个共享目录share. 右键共享,之后系统自动安装软件samba,之后共享出错: "net usershare"返回错误 255:net usershare: canno ...