Tag

计数+LIS, 二分+ST表, 计数+记搜

A. 改造二叉树

Description

题面

Solution

如果目标序列非严格递增,或者说目标序列是不下降的,那么答案就是 \(n\) 减去最长不下降子序列的长度。

比如这种情况:\(2\ 3\ 1\ 4\),\(LIS\) 为 \(2\ 3\ 4\),答案求出来为 \(1\),但由于整数的限制,应该要修改 \(2\) 次。即直接 \(LIS\) 求出的答案是在非严格递增的情况下的答案。

现在要求目标序列严格递增,一个常见的将严格递增整数序列映射成非严格递增整数序列的技巧就是将如下序列:

\(a_1, a_2, a_3, a_4 ... a_n\)

映射成:

\(a_1 - 1, a_2 - 2, a_3 - 3, a_4 - 4 ... a_n - n\)

这种方法常见于计数类问题。

这样映射后求最长不下降子序列的长度就没问题了。

考虑证明这个做法的正确性,如果 \(O(n^2)\) 转移 \(LIS\) 的话,每次从位置 \(i\) 转移到位置 \(j\) 时,只有满足 \(a[j]-a[i] \geq j-i\) 才能转移,而整体减去 \(\{1, 2, 3, ..., n\}\) 正是对这个限制的体现。

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std; const int N = 1e5 + 3;
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 < '0' || ch > '9');
int res = ch - 48;
while (ch = getchar(), ch >= '0' && ch <= '9') res = res * 10 + ch - 48;
return res;
} void Bfs() {
int x; stk[top = 1] = 1;
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 = 1; i <= n; ++i) a[i] = read();
for (int i = 2; i <= n; ++i) {
fa = read(); d = read();
(d ? rc[fa] : lc[fa]) = i;
}
Bfs();
f[qr = 1] = b[1];
for (int i = 2; i <= n; ++i) {
if (b[i] >= f[qr]) f[++qr] = b[i];
else {
l = 1; r = qr;
while (l <= r) {
mid = l + r >> 1;
if (f[mid] <= b[i]) l = mid + 1;
else r = mid - 1;
}
f[l] = b[i];
}
}
cout << n - qr << endl;
fclose(stdin); fclose(stdout);
return 0;
}

B. 数字对

Description

题面

Solution 1

二分区间长度,枚举左端点,显然可能的 \(a_k\) 就是区间最小值,判断 \(a_k\) 是否能整除这个区间的所有数就是判断 \(a_k\) 是否与这个区间的 \(gcd\) 相等,\(ST\)表维护区间最小值和区间\(gcd\)。

#include <cstdio>
#include <cmath> const int N = 500005;
int f[N][21], g[N][21], ans[N], n; //区间最小值,区间gcd int read() {
int x = 0; char c = getchar();
while (c < '0' || c > '9') c = getchar();
while (c >= '0' && c <= '9') {
x = (x << 3) + (x << 1) + (c ^ 48);
c = getchar();
}
return x;
}
int min(int x, int y) {
return x < y ? x : y;
}
int gcd(int a, int b) {
return !b ? a : gcd(b, a % b);
}
int Min(int l, int r) {
int k = log2(r - l + 1);
return min(f[l][k], f[r-(1<<k)+1][k]);
}
int Gcd(int l, int r) {
int k = log2(r - l + 1);
return gcd(g[l][k], g[r-(1<<k)+1][k]);
}
bool check(int k) {
int sta[N] = {}, top = 0;
for (int i = 1; i + k - 1 <= n; ++i)
if (Min(i, i + k - 1) == Gcd(i, i + k - 1))
sta[++top] = i;
if (!top) return false;
ans[0] = top;
for (int i = 1; i <= top; ++i) ans[i] = sta[i];
return true;
} int main() {
freopen("pair.in", "r", stdin);
freopen("pair.out", "w", stdout); n = read();
for (int i = 1; i <= n; ++i) f[i][0] = g[i][0] = read();
for (int j = 1; j <= 19; ++j)
for (int i = 1; i + (1 << j) - 1 <= n; ++i) {
f[i][j] = min(f[i][j-1], f[i+(1<<j-1)][j-1]);
g[i][j] = gcd(g[i][j-1], g[i+(1<<j-1)][j-1]);
}
int l = 0, r = n;
while (l < r) { //二分区间长度
int mid = l + (r - l + 1 >> 1);
if (check(mid)) l = mid;
else r = mid - 1;
}
printf("%d %d\n", ans[0], l - 1);
for (int i = 1; i <= ans[0]; ++i) printf("%d ", ans[i]); fclose(stdin);
fclose(stdout);
return 0;
}

Solution 2

然后还跟 \(Asia\) 学了一个除排序以外 \(O(n)\) 的做法…… Orz

大体思路是:

先从小到大排序,然后一个一个作为 \(a_k\) 向左右两边拓展

拓展到的点打上标记,表示不会从这个点开始拓展

这样的话每个点最多只会被它左边或者右边的一个点拓展到,所以复杂度是 \(O(n)\) 的

为什么是 \(O(n)\) 的呢,考虑三个位置 \(x,y,z\),\(y \leq x, z = x * y\),现用 \(x,y\) 去拓展 \(z\),如果 \(x\) 是 \(y\) 的倍数,那么 \(x\) 会被 \(y\) 拓展到,也就不能再去拓展 \(z\) 了;如果 \(x\) 不是 \(y\) 的倍数,\(x\) 拓展到 \(y\) 就会停止,也不会去拓展 \(z\)。

由此可知,每个元素只会被它左边的一个点和它右边的一个点拓展到,所以除排序外的复杂度为 \(O(n)\)。

C. 交换

Description

给定一个 \(\{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[q_0],s[q_0 + 1]\)
  2. 交换 \(s[q_1],s[q_1 + 1]\)

最后能使得排列 \(s = p\)。

问有多少个优美的排列,答案对 \(10^9+7\) 取模。\(n \leq 50\)

Solution

一个很厉害的计数\(DP\)。

考虑倒着处理, 比如交换 \((i, i + 1)\), 那么前面的所有数不管怎么交换都无法到后面去,后面的数也是一样到不了前面。说明这最后一次交换前,就要求对于所有的 \(x <= i, y > i\),\(p_x<p_y\)。所以交换前左边的数是连续的,右边也是连续的。由于交换前,前面和后面的数是互相不干涉的,所以就归结成了两个子问题。于是我们可以用记忆化搜索来解决这个问题。

设 \(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, k - 1)
\]

即前面的 \(k\) 个元素与后面的 \(n - k\) 个元素是两个独立的子问题,前面是 \(\{low ... low + k - 1\}\) 的排列,后面是 \(\{low + k ... low + n - 1\}\) 的排列,\(C(n - 2, k - 1)\) 代表的是在交换 \((k, k + 1)\) 前左右两边一共还要进行 \(n - 2\) 次交换,而每次交换左边与交换右边是不同方案,这相当于 \(n - 2\) 个位置选择 \(k - 1\) 个位置填入,故还需要乘上 \(C(n - 2, k - 1)\)。

时间复杂度为 \(O(n^4)\)。

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std; typedef long long ll;
const int N = 52, Mod = 1e9 + 7;
int n, p[N], dp[N][N], C[N][N]; int Dfs(int len, int low) {
if (dp[len][low] != -1) return dp[len][low];
if (len == 1) return dp[len][low] = 1;
int &res = dp[len][low]; res = 0;
int t[N], m = 0, j, k;
for (int i = 1; i <= n; ++i)
if (p[i] >= low && p[i] < low + len)
t[++m] = p[i];
for (int i = 1; i < m; ++i) {
swap(t[i], t[i + 1]);
for (j = 1; j <= i; ++j)
if (t[j] >= low + i) break;
for (k = i + 1; 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 - 2][i - 1] % Mod;
res = (res + tmp) % Mod;
}
swap(t[i], t[i + 1]);
}
return res;
} int main() {
freopen("swap.in", "r", stdin);
freopen("swap.out", "w", stdout);
scanf("%d", &n);
for (int i = 1; i <= n; ++i) scanf("%d", &p[i]);
memset(dp, -1, sizeof(dp));
for (int i = 0; i <= n; ++i) {
C[i][0] = 1;
for (int j = 1; j <= i; ++j)
C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % Mod;
}
Dfs(n, 0);
if (dp[n][0] != -1) cout << dp[n][0] << endl;
else puts("0");
fclose(stdin); fclose(stdout);
return 0;
}

「NOIP模拟赛」Round 3的更多相关文章

  1. 「NOIP模拟赛」Round 2

    Tag 递推,状压DP,最短路 A. 篮球比赛1 题面 \(Milky\ Way\)的代码 #include <cstdio> const int N = 2000, xzy = 1e9 ...

  2. 「NOIP模拟赛」数位和乘积(dp,高精)

    统计方案数,要么组合数,要么递推(dp)了. 这是有模拟赛历史以来爆炸最狠的一次 T1写了正解,也想到开long long,但是开错了地方然后数组开大了结果100->0 T3看错题本来简单模拟又 ...

  3. 「CSP-S模拟赛」2019第四场

    「CSP-S模拟赛」2019第四场 T1 「JOI 2014 Final」JOI 徽章 题目 考场思考(正解) T2 「JOI 2015 Final」分蛋糕 2 题目 考场思考(正解) T3 「CQO ...

  4. 「CSP-S模拟赛」2019第三场

    目录 T1 「POI2007」山峰和山谷 Ridges and Valleys 题目 考场思路(几近正解) 正解 T2 「JOI 2013 Final」 现代豪宅 题目 考场思路(正解) T3 「SC ...

  5. 【模拟】HHHOJ#251. 「NOIP模拟赛 伍」高精度

    积累模拟经验 题目描述 维护一个二进制数,支持如下操作 "+" 该数加 11 "-" 该数减 11 "*" 该数乘 22 "\&q ...

  6. Solution -「牛客 NOIP 模拟赛」打拳

    \(\mathcal{Description}\)   现 \(2^n\) 个人进行淘汰赛,他们的战力为 \(1\sim 2^n\),战力强者能战胜战力弱者,但是战力在集合 \(\{a_m\}\) 里 ...

  7. 「CSP-S模拟赛」2019第二场

    目录 T1 Jam的计数法 题目 考场思路(正解) T2 「TJOI / HEOI2016」排序 题目 考场思路(假正解) 正解 T3 「THUWC 2017」随机二分图 题目 考场思路 正解 这场考 ...

  8. 「CSP-S模拟赛」2019第一场

    目录 T1 小奇取石子 题目 考场思路 正解 T2 「CCO 2017」专业网络 题目 考场思路 题解 T3 「ZJOI2017」线段树 题目 考场思路 正解 这场考试感觉很奇怪. \(T1.T2\) ...

  9. 「2018-11-05模拟赛」T5 传送机 解题报告

    5.传送机(sent.*) 问题描述: 黄黄同学要到清华大学上学去了.黄黄同学很喜欢清华大学的校园,每次去上课时总喜欢把校园里面的每条路都走一遍,当然,黄黄同学想每条路也只走一遍. 我们一般人很可能对 ...

随机推荐

  1. Operating System:管程相关概念

    管程 (Moniters,也称为监视器)一.管程的概念是一种程序结构,结构内的多个子程序(对象或模块)形成的多个工作线程互斥访问共享资源.这些共享资源一般是硬件设备或一群变量.管程实现了在一个时间点, ...

  2. IO - 同步,异步,阻塞,非阻塞 (亡羊补牢篇)【转】

    同步(synchronous) IO和异步(asynchronous) IO,阻塞(blocking) IO和非阻塞(non-blocking)IO分别是什么,到底有什么区别? 详细请看下链接: IO ...

  3. 云原生系列1 pod基础

    POD解决了什么问题? 成组资源调度问题的解决. mesos采用的资源囤积策略容易出现死锁和调度效率低下问题:google采用的乐观调度技术难度非常大: 而k8s使用pod优雅的解决了这个问题. po ...

  4. markdown table collapse span

    markdown table collapse span refs xgqfrms 2012-2020 www.cnblogs.com 发布文章使用:只允许注册用户才可以访问! 原创文章,版权所有️x ...

  5. Visual Studio Online & Web 版 VS Code

    Visual Studio Online & Web 版 VS Code https://online.visualstudio.com https://devblogs.microsoft. ...

  6. 云原生系列6 基于springcloud架构风格的本地debug实现

    debug是程序员在日常开发中最常使用的操作, 那么,你是如何快速在微服务架构风格下快速debug后端服务呢? 开发现状 开发的理想状态 本地调测的使用步骤 登录智能网关 如果集成开发环境是在本地局域 ...

  7. Error: Actions must be plain objects. Use custom middleware for async actions.

    原本代码: import { SREACH_FOCUS, SREACH_BLUR } from "./actionType" export const searchFocus = ...

  8. 维格表2月更新:智能图表上线,唤醒全量工作数据堪比AI

    你是否曾经想过,你的维格表数据有朝一日变化出如科幻电影般的数据图表? 你是否感到厌倦,对当前的后台系统密密麻麻的数据累觉不爱? 你是否一直期待,拥有一个专属大数据 BI 看板,让你的规划如有神助,挥斥 ...

  9. (原创)用.NET Core实现微信自动回复工具(上篇)

    全文 没有视频的介绍显得尤为空白仓促.所以,如果你不赶时间,看看视频先 → → http://wexin.fuyue.xyz/Resource/Video/wechatTool.mp4 ← ← 功能列 ...

  10. CVer想知道的都在这里了,一起分析下《中国计算机视觉人才调研报告》吧!

    最近闲来无事,老潘以一名普通算法工程师的角度,结合自身以及周围人的情况,理性也感性地分析一下极市平台前些天发布的2020年度中国计算机视觉人才调研报告. 以下的"计算机视觉人才"简 ...