[NOIP2016提高]蚯蚓 题解
题目地址:ė …
May all the beauty be blessed.
题目地址:洛谷:【P4331】[BOI2004]Sequence 数字序列 – 洛谷、BZOJ:Problem 1367. — [Baltic2004]sequence
输入格式:
输出格式:
第一行输出最小的绝对值之和。
第二行输出序列 bi ,若有多种方案,只需输出其中一种。
输入样例#1:
5 2 5 46 12 1
输出样例#1:
47 2 5 11 12 13
【数据范围】
40%的数据 n≤5000
60%的数据 n≤300000
100%的数据 n≤1000000 , 0≤a_i≤2×10^9
考虑当所求序列$z_i$是不降的情况应该如何解决。我们有两种情况:
因此,我们可以将$t$数列分为若干区间,每个区间的答案即其中位数,从而解决这个问题。
然而题目让我们求一个严格递增的序列,我们考虑将$t_i$处理成$t_i – i$,就可以用跟上面一致的方法解决这个问题,因为处理完毕后的数列不降等价于原数列单增。
实现上,我们对每个元素建一个可并堆,然后向左合并左边元素构成的可并堆(即左边的区间),直到这个区间的中位数不小于左边相邻区间的中位数。中位数可以利用大根堆维护,让堆的大小不大于$\left\lceil \frac{|S|}{2} \right\rceil$再取堆顶即可。
复杂度$O(n \log n)$。
// Code by KSkun, 2018/7
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
typedef long long LL;
inline char fgc() {
static char buf[100000], *p1 = buf, *p2 = buf;
return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2)
? EOF : *p1++;
}
inline LL readint() {
register LL res = 0, neg = 1; register char c = fgc();
for(; !isdigit(c); c = fgc()) if(c == '-') neg = -1;
for(; isdigit(c); c = fgc()) res = (res << 1) + (res << 3) + c - '0';
return res * neg;
}
const int MAXN = 1000005;
int n, a[MAXN];
struct Node {
int ch[2], fa, val, dis, siz;
} tr[MAXN];
int rt[MAXN], l[MAXN], r[MAXN], htot;
inline int merge(int x, int y) {
if(!x || !y) return x + y;
if(tr[x].val < tr[y].val) std::swap(x, y);
tr[x].ch[1] = merge(tr[x].ch[1], y);
tr[x].siz = tr[tr[x].ch[0]].siz + tr[tr[x].ch[1]].siz + 1;
tr[tr[x].ch[1]].fa = x;
if(tr[tr[x].ch[0]].dis < tr[tr[x].ch[1]].dis) std::swap(tr[x].ch[0], tr[x].ch[1]);
tr[x].dis = tr[tr[x].ch[1]].dis + 1;
return x;
}
int main() {
n = readint();
for(int i = 1; i <= n; i++) {
a[i] = readint() - i;
}
for(int i = 1; i <= n; i++) {
htot++; rt[htot] = i;
l[htot] = r[htot] = i;
tr[i].val = a[i]; tr[i].siz = 1;
while(htot > 1 && tr[rt[htot]].val < tr[rt[htot - 1]].val) {
htot--;
rt[htot] = merge(rt[htot], rt[htot + 1]);
r[htot] = r[htot + 1];
while(tr[rt[htot]].siz * 2 > r[htot] - l[htot] + 2) {
rt[htot] = merge(tr[rt[htot]].ch[0], tr[rt[htot]].ch[1]);
}
}
}
LL ans = 0;
for(int i = 1; i <= htot; i++) {
int w = tr[rt[i]].val;
for(int j = l[i]; j <= r[i]; j++) {
ans += abs(w - a[j]);
}
}
printf("%lld\n", ans);
for(int i = 1; i <= htot; i++) {
int w = tr[rt[i]].val;
for(int j = l[i]; j <= r[i]; j++) {
printf("%d ", w + j);
}
}
return 0;
}
题目地址:洛谷:【P1954】[NOI2010]航空管制 – 洛谷、BZOJ:Problem 2535. — [Noi2010]Plane 航空管制2
世博期间,上海的航空客运量大大超过了平时,随之而来的航空管制也频频发生。最近,小X就因为航空管制,连续两次在机场被延误超过了两小时。对此,小X表示很不满意。
在这次来烟台的路上,小X不幸又一次碰上了航空管制。于是小X开始思考关于航空管制的问题。
假设目前被延误航班共有n个,编号为1至n。机场只有一条起飞跑道,所有的航班需按某个顺序依次起飞(称这个顺序为起飞序列)。定义一个航班的起飞序号为该航班在起飞序列中的位置,即是第几个起飞的航班。
起飞序列还存在两类限制条件:
小X思考的第一个问题是,若给定以上两类限制条件,是否可以计算出一个可行的起飞序列。第二个问题则是,在考虑两类限制条件的情况下,如何求出每个航班在所有可行的起飞序列中的最小起飞序号。
输入格式:
输入文件plane.in第一行包含两个正整数n和m,n表示航班数目,m表示第二类限制条件(相对起飞顺序限制)的数目。
第二行包含n个正整数k1, k2, …, kn。
接下来m行,每行两个正整数a和b,表示一对相对起飞顺序限制(a, b),其中1≤a,b≤n, 表示航班a必须先于航班b起飞。
输出格式:
输出文件plane.out由两行组成。
第一行包含n个整数,表示一个可行的起飞序列,相邻两个整数用空格分隔。输入数据保证至少存在一个可行的起飞序列。如果存在多个可行的方案,输出任意一个即可。
第二行包含n个整数t1, t2, …, tn,其中ti表示航班i可能的最小起飞序号,相邻两个整数用空格分隔。
输入样例#1:
5 5 4 5 2 5 4 1 2 3 2 5 1 3 4 3 1
输出样例#1:
3 5 1 4 2 3 4 1 2 1
输入样例#2:
5 0 3 3 3 5 5
输出样例#2:
3 2 1 5 4 1 1 1 4 4
【样例说明】
在样例1 中:
起飞序列3 5 1 4 2满足了所有的限制条件,所有满足条件的起飞序列有:
3 4 5 1 2 3 5 1 2 4 3 5 1 4 2 3 5 4 1 2
5 3 1 2 4 5 3 1 4 2 5 3 4 1 2
由于存在(5, 1)和(3, 1)两个限制,航班1只能安排在航班5和3之后,故最早起飞时间为3,其他航班类似。
在样例2 中:
虽然航班4、5没有相对起飞顺序限制,但是由于航班1、2、3都必须安排在前3个起飞,所以4、5最早只能安排在第4个起飞。
【数据范围】
对于30%数据:n≤10;
对于60%数据:n≤500;
对于100%数据:n≤2,000,m≤10,000。
第一问,考虑把限制条件建个图,一定是一个DAG,直接进行拓扑排序,只不过拓扑的队列要改成优先队列,优先级按最晚时间限制从小到大排序,这样出来的顺序就是正确的了。
但是考虑第二问的时候,发现就算建反图DFS还可能出现其他需要满足的条件,例如样例2中前三个飞机必须在前三飞。此时我们考虑倒着找,建反图按限制从大到小的顺序拓扑,我们枚举每个飞机u,每次都拓扑一遍,遇到u的时候直接把它设置为不可以被加入队列中,这样就可以无视掉u的前置,直到条件不能被满足位置。倒着拓扑的意义,实质上是在求满足了u以后剩下最多的点。用n减去这个数字就得到了我们想要的答案。
这个做法的复杂度是O(n^2 \log n)的,在洛谷得开O2才能跑过。
// Code by KSkun, 2018/5
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
#include <queue>
#include <vector>
typedef long long LL;
inline char fgc() {
static char buf[100000], *p1 = buf, *p2 = buf;
return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF
: *p1++;
}
inline LL readint() {
register LL res = 0, neg = 1;
register char c = fgc();
while(!isdigit(c)) {
if(c == '-') neg = -1;
c = fgc();
}
while(isdigit(c)) {
res = (res << 1) + (res << 3) + c - '0';
c = fgc();
}
return res * neg;
}
const int MAXN = 20005;
std::vector<int> gra[MAXN];
int deg[MAXN];
inline void addedge(int u, int v) {
gra[u].push_back(v); deg[v]++;
}
int n, m, lim[MAXN];
struct Node {
int u, lim;
inline bool operator<(const Node &rhs) const {
return lim < rhs.lim;
}
};
int now[MAXN];
int ans[MAXN];
inline void toposort() {
std::priority_queue<Node> pq;
memcpy(now, deg, sizeof(deg));
for(int i = 1; i <= n; i++) {
if(!now[i]) pq.push(Node {i, lim[i]});
}
int cnt = n;
while(!pq.empty()) {
int u = pq.top().u; pq.pop();
ans[cnt--] = u;
for(int i = 0; i < gra[u].size(); i++) {
int v = gra[u][i];
if(!--now[v]) {
pq.push(Node {v, lim[v]});
}
}
}
}
inline int toposort1(int uu) {
std::priority_queue<Node> pq;
memcpy(now, deg, sizeof(deg));
now[uu] = n;
for(int i = 1; i <= n; i++) {
if(!now[i]) pq.push(Node {i, lim[i]});
}
for(int i = n; i; i--) {
if(pq.empty() || pq.top().lim < i) return i;
int u = pq.top().u; pq.pop();
for(int i = 0; i < gra[u].size(); i++) {
int v = gra[u][i];
if(!--now[v]) pq.push(Node {v, lim[v]});
}
}
}
int main() {
n = readint(); m = readint();
for(int i = 1; i <= n; i++) {
lim[i] = readint();
}
for(int i = 1, u, v; i <= m; i++) {
u = readint(); v = readint();
addedge(v, u);
}
toposort();
for(int i = 1; i <= n; i++) {
printf("%d ", ans[i]);
}
printf("\n");
for(int i = 1; i <= n; i++) {
printf("%d ", toposort1(i));
}
return 0;
}
题目地址:BZOJ:Problem 5179. — [Jsoi2011]任务调度
一台超级计算机共有N颗CPU。现在这台超级计算机有M个任务要做,但同时还要考虑到不能让CPU过热。所幸的是这台超级计算机已经将任务安排好了,现在要做的只是请你根据安排好的指令来模拟它的工作过程。一开始,这N颗CPU都没有被分配任何的任务。之后,会给你以下几类指令(CPU的编号为1到N的整数,任务的编号为1到M的整数)
指令格式 作用
ADD n k w 将 k 号任务(权值为 w)分配给 n 号 CPU
DEC n k w 将 k 号任务的权值减少 w(已知 k 号任务被分配给了 n 号 CPU)
TRANS n1 n2 将分配给 n1 号 CPU 的任务全部转移给 n2 号 CPU
MIN n 输出分配给 n 号 CPU 的任务中权值最小的任务的权值
WORK n w 将分配给 n 号 CPU 的任务中权值最小的任务的权值加上 w,如果权值最小的任务不唯一,则不更改权值,并输出一行“ ERROR”
输入格式:
包含N+1行。
第1行包含三个正整数N、M、K,分别表示CPU的数目、任务数和指令数。
第2行到K+1行,每行包含一条指令。
N≤500, M≤300000, K≤300000。
保证任务的权值在 32 位有符号整型的范围内。
保证一个任务只会被分配一次(即至多被 ADD 一次)。
保证 ADD 指令、DEC 指令和 WORK 指令中的 w 是非负整数。
保证 TRANS 指令的两个参数不相同。
输出格式:
若干行,其中包括MIN语句的输出和“ERROR”输出,每个输出占一行
输入样例#1:
2 3 13 ADD 1 2 100 ADD 1 1 90 MIN 1 WORK 1 20 TRANS 1 2 MIN 2 ADD 1 3 105 TRANS 2 1 MIN 1 DEC 1 3 200 MIN 1 DEC 1 1 205 WORK 1 15
输出样例#1:
90 100 100 -95 ERROR
考虑左偏树来做这个题。ADD即新建节点并且合并到该CPU堆。DEC即先删点,改完权值以后再并回去。TRANS即合并两个堆。MIN即询问堆中最小值。WORK同DEC。
// Code by KSkun, 2018/4
#include <cstdio>
#include <cctype>
#include <algorithm>
typedef long long LL;
inline char fgc() {
static char buf[100000], *p1 = buf, *p2 = buf;
return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF
: *p1++;
}
inline LL readint() {
register LL res = 0, neg = 1;
char c = fgc();
while(c < '0' || c > '9') {
if(c == '-') neg = -1;
c = fgc();
}
while(c >= '0' && c <= '9') {
res = res * 10 + c - '0';
c = fgc();
}
return res * neg;
}
inline char readop() {
char c;
while(!isalpha(c = fgc()));
return c;
}
const int MAXN = 300005;
struct Node {
int fa, val, dis, ch[2];
} tr[MAXN];
inline int merge(int u, int v) {
if(!u || !v) return u + v;
if(tr[u].val > tr[v].val) std::swap(u, v);
tr[u].ch[1] = merge(tr[u].ch[1], v);
tr[tr[u].ch[1]].fa = u;
if(tr[tr[u].ch[0]].dis < tr[tr[u].ch[1]].dis) std::swap(tr[u].ch[0], tr[u].ch[1]);
tr[u].dis = tr[tr[u].ch[1]].dis + 1;
return u;
}
inline bool isleft(int p) {
return tr[tr[p].fa].ch[0] == p;
}
inline void erase(int p) {
tr[tr[p].ch[0]].fa = tr[tr[p].ch[1]].fa = 0;
int q = merge(tr[p].ch[0], tr[p].ch[1]);
if(q) tr[q].fa = tr[p].fa; if(tr[p].fa) tr[tr[p].fa].ch[!isleft(p)] = q;
for(int i = tr[p].fa; i; i = tr[i].fa) {
if(tr[tr[i].ch[0]].dis < tr[tr[i].ch[1]].dis) std::swap(tr[i].ch[0], tr[i].ch[1]);
if(tr[i].dis == tr[tr[i].ch[1]].dis + 1) break;
tr[i].dis = tr[tr[i].ch[1]].dis + 1;
}
tr[p].fa = tr[p].ch[0] = tr[p].ch[1] = tr[p].dis = 0;
}
inline int poprt(int p) {
tr[tr[p].ch[0]].fa = tr[tr[p].ch[1]].fa = 0;
int q = merge(tr[p].ch[0], tr[p].ch[1]);
tr[p].fa = tr[p].ch[0] = tr[p].ch[1] = tr[p].dis = 0;
return q;
}
int n, m, k, x, y, z, rt[MAXN];
char op;
int main() {
tr[0].dis = -1;
n = readint(); m = readint(); k = readint();
while(k--) {
op = readop();
if(op == 'A') {
x = readint(); y = readint(); z = readint();
tr[y].val = z;
rt[x] = merge(y, rt[x]);
} else if(op == 'D') {
x = readint(); y = readint(); z = readint();
if(!tr[y].fa) rt[x] = poprt(y);
else erase(y);
tr[y].val -= z;
rt[x] = merge(rt[x], y);
} else if(op == 'T') {
x = readint(); y = readint();
rt[y] = merge(rt[x], rt[y]);
rt[x] = 0;
} else if(op == 'M') {
x = readint();
printf("%d\n", tr[rt[x]].val);
} else {
x = readint(); y = readint(); int p = rt[x];
if((tr[p].ch[0] && tr[tr[p].ch[0]].val == tr[p].val)
|| (tr[p].ch[1] && tr[tr[p].ch[1]].val == tr[p].val)) {
puts("ERROR");
continue;
}
rt[x] = poprt(p);
tr[p].val += y;
rt[x] = merge(rt[x], p);
}
}
return 0;
}