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 ...
随机推荐
- 使用阿里云生成的pem密钥登录
我用的阿里云生成的ssh密钥,服务器上已有公钥,私钥为.pem文件,下载在本地,网上都说要转换为.ppk再用,其实用secure不必转换 一..pem和.ppk文件区别 .pem 密钥通用格式 .p ...
- 分母为0的坑(float)
分母不能为0 对于int 类型,如果分母为0,在程序运行时,会报错. 而对于float 类型,如果分母为0,则不会报错,而是会返回一个infinity(无穷大),也就是NAN. 因为除一个无穷小的数, ...
- 04.UTXO:未使用的交易输出,比特币核心概念之一
在比特币系统上其实并不存在“账户”,而只有“地址”.只要你愿意,你就可以在比特币区块链上开设无限多个钱包地址,你拥有的比特币数量是你所有的钱包地址中比特币的总和.比特币系统并不会帮你把这些地址汇总起来 ...
- 笔记8:Linux知识
linux学习笔记 1 Linux介绍 1.1 基本知识 应用领域:服务器使用 LAMP(Linux + Apache + MySQL + PHP)或 LNMP(Linux + Nginx+ MySQ ...
- vue-router有哪几种导航钩子 keep-alive的详细用法 解决跨域
1===>vue-router有哪几种导航钩子? 第一种:是全局导航钩子:router.beforeEach(to,from,next) 第二 ...
- CentOS最小化安装后找不到ifconfig命令
1.ifconfig命令是设置或显示网络接口的程序,可以显示出我们机器的网卡信息, 可是有些时候最小化安装CentOS等Linux发行版的时候会默认不安装ifconfig等命令, 这时候你进入终端,运 ...
- 源码解读:webdriver client的原理
前言 又到年底了,群里很多朋友说要开始备战2020金三银四,其实,我建议是,如果你不是技术大牛,就不要去凑热闹. 其实,现在(11,12月份)就是最佳换工作的时候,因为很多人想等着拿了年终再走,虽然招 ...
- 如何隐藏WooCommerce Shop Page页面的标题
有时我们不想显示WooCommerce Shop Page页面标题,如下图所示,需要如何操作呢?随ytkah一起来看看吧.在主题function.php文件中添加下面的代码就可以隐藏了 add_fil ...
- opencv估计两图的三维坐标变换矩阵
cv::estimateAffine3D(MatFrom, MatTo, Transfrom, inlier); Transform得到的是重MatFrom到MatTo的变换矩阵.inlier给一个空 ...
- Cookie 技术
Cookie 学习: 问题: HTTP 协议是没有记忆功能的,一次请求结束后,相关数据会被销毁.如果第二次的请求需要使用相同的请求数据怎么办呢?难道是让用户再次请求书写吗? 解决:使用 Cookie ...