CSP复赛day2模拟题
没错,我又爆零了、、、、、先让我自闭一分钟。。。。。so
当你忘记努力的时候,现实会用一记响亮的耳光告诉你东西南北在哪。
好了,现在重归正题:
全国信息学奥林匹克联赛(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.第 3页 共 4页
上面那题,我不会啊。。那还是先粘一片正解吧
T1:
20% :暴力。
40% :可以用 DP 或者贪心或者神奇的暴力等其他奇怪的方法完成。
60% :正解的 LIS 打成 O(n ^ 2)。
100% :首先求出这颗二叉树的中序遍历,那么问题就转换成用最少的修改次数使这个整
数序列严格单调递增。于是很自然的想到了 LIS,但单纯用 LIS 是有一些问题的,
比如这种情况:2 3 1 4, LIS 为 2 3 4,答案求出来为 1,但由于整数的限制,应该
要修改 2 次。即直接 LIS 求出的答案是在非严格递增的情况下的答案。
所以我们将原序列稍加修改, 一个常见的将严格递增整数序列映射成非严格递增整
数序列的技巧就是将如下序列:
a1, a2, a3, a4 ... an 映射成:
a1 - 1, a2 - 2, a3 - 3, a4 - 4 ... an - n.
(这种方法常见于计数类问题)。
这样映射后求最长不下降子序列的长度就没问题了。
here:
#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:
30% :暴力枚举判断。O(n^4)。
60% :特殊区间的特点实际上就是区间最小值等于这个区间的 GCD,于是暴力或递推算
出每个区间的最小值与 GCD。而对于最大价值,可以通过二分来进行求解。复杂
度 O(n ^ 2)。
100%:在 60%的基础上,最小值与 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
这是我颇为得意的一题--但还是没过--DFS回溯打错了,忘记交换回来了,这告诉我们打DFS在return时不仅要改book[i],还要把其他的改变的也改回来,让它返璞归真,以它该有的样子进行下一个状态的search。
先粘我的代码(经过周同学的“美化”):
#include<iostream>
#include<string>
using namespace std;//n值比较小 用DFS做
//string 大法好
// 30分
const int P = 1e9 + ; //模数可以声明成常量,这样写比较好看,也比较好懂 ,大众平时也这样用
int n, book[];
long long ans;
string p, s; //声明全局变量,可以节省内存,也可以避免传参错误,但是这样在不注意的情况下会导致错误 void dfs(int k) { //cnt the number of change
if(k == n - ) { if(s == p) ans++; return; }
for(int i = ; i <= n - ; i++) {
if(book[i] == ) {
book[i] = ;
swap(s[i], s[i + ]);
dfs(k + );
swap(s[i], s[i + ]);//big problem就是这,害我爆零。。还找了半天才找到
book[i] = ;
}
}
} int main() {
cin >> n;
for(int i = ; i < n; i++) {
char a;
cin >> a;
p = p + a;
}
for(int i = ; i < n; i++) s = s + char('' + i);
dfs();
cout << ans % P;
return ;
} /*
大括号要写整齐,
代码好不好看大部分取决于大括号的整齐程度,缩进真的无所谓,但大括号一定要整齐 */
正解:
T3:
30%:
枚举所有排列,判定即可。
100%:
考虑倒着处理, 比如交换 (i, i + 1), 那么前面的所有数不管怎么交换都无法到后面去(下
标恒小于等于 i),后面的数也是一样到不了前面。说明这最后一次交换前,就要求对于所有
的 x <= i, y > i,px<py。所以交换前左边的数是连续的,右边也是连续的。由于交换前, 前
面和后面的数是互相不干涉的,所以就归结成了两个子问题。于是我们可以用记忆化搜索来
解决这个问题。
设 dp[n][low] 代表长度为 n,H 是{low, low + 1,…,low + n - 1}的排列,且 H 是 p的子序
列,在 H 上优美序列的个数。
我们枚举交换哪两个相邻元素(k,k+1), 然后判断 k 前面的所有数是否都小于后面的所有
数,如果是则进行转移 dp[n][low] += dp[k][low] * dp[n – k][low + k ] * C(n – 2, n – 1 - k)。
即前面的 k 个元素与后面的 n - k 个元素是两个独立的子问题,前面是{low ... low + k - 1}的
排列,后面是{low + k ... low + n - 1}的排列,C(n - 2, n - 1 - k)代表的是在交换(k, k + 1)前左
右两边还分别要进行 n - 2次交换,而每次交换左边与交换右边是不同方案,这相当于 n - 2
个位置选择 n - 1 - k 个位置填入,故还需要乘上 C(n - 2, n - 1 - k)。时间复杂度为 O(n^4)。
#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 ;
}
CSP复赛day2模拟题的更多相关文章
- 全国信息学奥林匹克联赛 ( NOIP2014) 复赛 模拟题 Day1 长乐一中
题目名称 正确答案 序列问题 长途旅行 英文名称 answer sequence travel 输入文件名 answer.in sequence.in travel.in 输出文件名 answer. ...
- poj 1008:Maya Calendar(模拟题,玛雅日历转换)
Maya Calendar Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 64795 Accepted: 19978 D ...
- poj 1888 Crossword Answers 模拟题
Crossword Answers Time Limit: 1000MS Memory Limit: 30000K Total Submissions: 869 Accepted: 405 D ...
- CodeForces - 427B (模拟题)
Prison Transfer Time Limit: 1000MS Memory Limit: 262144KB 64bit IO Format: %I64d & %I64u Sub ...
- sdut 2162:The Android University ACM Team Selection Contest(第二届山东省省赛原题,模拟题)
The Android University ACM Team Selection Contest Time Limit: 1000ms Memory limit: 65536K 有疑问?点这里 ...
- UVALive 4222 Dance 模拟题
Dance 题目连接: https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&pag ...
- cdoj 25 点球大战(penalty) 模拟题
点球大战(penalty) Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://acm.uestc.edu.cn/#/problem/show/2 ...
- Educational Codeforces Round 2 A. Extract Numbers 模拟题
A. Extract Numbers Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/contest/600/pr ...
- URAL 2046 A - The First Day at School 模拟题
A - The First Day at SchoolTime Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://acm.hust.edu.cn/vjudg ...
随机推荐
- adb shell提示“adb server is out of date.” 解决方法
[问题描述] 1.打开运行:window+R,输入cmd回车 2.使用命令 adb shell 无法登录安卓设备,提示如下: adb server is out of date. killing.. ...
- Spring(004)-Bean装配
一,问题,Bean找不到 代码 @Component public class DemoClass { public int doSth() { ; } } 测试代码 @RunWith(SpringJ ...
- 201871010136 -赵艳强《面向对象程序设计(java)》第十六周学习总结
201871010136-赵艳强<面向对象程序设计(java)>第十六周学习总结 项目 内容 这个作业属于哪个课程 <任课教师博客主页链接>https://www.cnbl ...
- Tkinter 鼠标键盘事件(二)
一个Tkinter主要跑在mainloop进程里.Events可能来自多个地方,比如按键,鼠标,或是系统事件. Tkinter提供了丰富的方法来处理这些事件.对于每一个控件Widget,你都可以为其绑 ...
- Xamarin.Forms移动开发系列3:项目剖析
摘要 本文主要进行Xamarin.Forms应用程序剖析. 前言 本文介绍Xamarin.Forms应用程序剖析. 由于本系列重点研究对象为Xamarin.Forms,所以对Xamarin.Andro ...
- 【领会要领】web前端-轻量级框架应用(jQuery基础)
作者 | Jeskson 来源 | 达达前端小酒馆 jquery的安装和语法,jquery的多种选择器,dom操作和jquery事件. jQuery框架,简介,优势,安装,语法,jQuery选择器,i ...
- [LeetCode] 660. Remove 9 移除9
Start from integer 1, remove any integer that contains 9 such as 9, 19, 29... So now, you will have ...
- Manjaro Linux安装后第一件事
环境:Manjaro KDE 使用中科大源 USTC Mirror 当然也可以勾选所有中国镜像源 谷歌加上云pinyin拼音输入法,不亚于搜狗 fcitx-qt5 kcm-fcitx (confi ...
- 关于Design Complier/Library Compiler的跌坑(坑爹)记录
最近需要用DC做一些事,然后需要转库,中午偷个闲,特来记录一下中间的一些坎坷. 1.首先是要转库.我们只有.lib文件的格式,所以需要把.lib文件转换成.db格式.然后坑来了!!!DC2015及以后 ...
- oracle--查询速度慢
查询速度慢的原因很多,常见如下几种: 1, 没有索引或者没有用到索引(这是查询慢最常见的问题,是程序设计的缺陷) 2, I/O吞吐量小,形成了瓶颈效应. 3, 没有创建计算列导致查询不优化. 4, ...