没错,我又爆零了、、、、、先让我自闭一分钟。。。。。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模拟题的更多相关文章

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

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

  2. poj 1008:Maya Calendar(模拟题,玛雅日历转换)

    Maya Calendar Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 64795   Accepted: 19978 D ...

  3. poj 1888 Crossword Answers 模拟题

    Crossword Answers Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 869   Accepted: 405 D ...

  4. CodeForces - 427B (模拟题)

    Prison Transfer Time Limit: 1000MS   Memory Limit: 262144KB   64bit IO Format: %I64d & %I64u Sub ...

  5. sdut 2162:The Android University ACM Team Selection Contest(第二届山东省省赛原题,模拟题)

    The Android University ACM Team Selection Contest Time Limit: 1000ms   Memory limit: 65536K  有疑问?点这里 ...

  6. UVALive 4222 Dance 模拟题

    Dance 题目连接: https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&pag ...

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

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

  8. Educational Codeforces Round 2 A. Extract Numbers 模拟题

    A. Extract Numbers Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/contest/600/pr ...

  9. 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 ...

随机推荐

  1. Linux系统中的load average(平均负载/运行队列)

    1.load average 的含义 系统负载(System Load)是系统CPU繁忙程度的度量,即有多少进程在等待被CPU调度(进程等待队列的长度) linux系统中的Load对当前CPU工作量的 ...

  2. C++编译错误 --- 成员函数定义在 .h 文件中出现重定义错误(Error LNK 2005)

    今天写了一个简单的类,定义在 .h 文件中, 类很简单就将其成员函数定义在了一起(class类后面).运行的时候出现了如下图所示的编译错误(error LNK2005) 查资料,大部分都是说需要加上 ...

  3. JS高阶---继承模式(原型链继承)

    [前言] 之前已经介绍了对象创建的五种模式,下面看下继承模式 本节介绍下<原型链继承> [主体] 验证如下: 关键点: .

  4. IP、MAC和端口号(六)

    在茫茫的互联网海洋中,要找到一台计算机非常不容易,有三个要素必须具备,它们分别是 IP 地址.MAC 地址和端口号. 一.IP地址 IP地址是 Internet Protocol Address 的缩 ...

  5. 浙大&川大提出脉冲版ResNet:继承ResNet优势,实现当前最佳

    浙大&川大提出脉冲版ResNet:继承ResNet优势,实现当前最佳 选自arXiv,作者:Yangfan Hu等,机器之心编译. 脉冲神经网络(SNN)具有生物学上的合理性,并且其计算潜能和 ...

  6. Python进阶-VI 生成器函数进阶、生成器表达式、推导式

    一.生成器函数进阶 需求:求取移动平均数 1.应用场景之一,在奥运会气枪射击比赛中,每打完一发都会显示平均环数! def show_avg(): print('你已进入显示移动平均环数系统!') a ...

  7. yum 初始化国内

    修改为阿里源备份默认的yum源文件 mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup 下载阿里 ...

  8. 5.Vue的组件

    1.什么是组件 组件是可复用的Vue实例,也就是一组可以复用的模版,类似JSTL的自定义标签. 你可能会有页头.侧边栏.内容区等组件,每个组件又包含了其它的像导航链接.博文之类的组件. 2.第一个Vu ...

  9. [LeetCode] 88. Merge Sorted Array 混合插入有序数组

    Given two sorted integer arrays nums1 and nums2, merge nums2 into nums1 as one sorted array. Note: T ...

  10. okhttp 发送get post 请求

    package com.qlwb.business.util; import java.util.Map; import com.alibaba.fastjson.JSON; import okhtt ...