本题解包含题目:
555A |
555B |
555C |
555D |
A题:
n(2*10^5)个大小不同的套娃 按照小的能套在大的里面的规则 已经按输入顺序套好了m个 问 最少拆、套几次可以形成1在2里 2在3里……n-1在n里 这种情况
思路:
我们可以这样理解一次操作——将大套娃拆开取出里面、套上一个大号的套娃 很明显每一步都是针对当前要处理的套娃中最大的那个进行的 那么最终状态一定是找到最长的1…x连续序列 除了这个序列外所有套娃全拆开 再按顺序套好
代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<set>
#include<vector>
#include<queue>
#include<cstdlib>
#include<ctime>
#include<cmath>
using namespace std;
typedef long long LL;
#define N 200010
int n, m, now;
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; i++) {
int p;
scanf("%d", &p);
for (int j = 1; j <= p; j++) {
int x;
scanf("%d", &x);
if (x == j) now++;
}
}
printf("%d\n", n - now - m + 1 + n - now);
return 0;
}
B题:
数轴上有n(2*10^5)个不相交的区间和m(2*10^5)个桥 每座桥可以连接相邻两个区间[a,b]和[c,d]当且仅当桥长l<=d-a且l>=c-b 每座桥最多使用一次 求 建桥的方案
思路:
两个相邻区间可建桥的桥长范围是[c-b,d-a] 将这些范围和桥排序 很明显对于一座桥 要么不使用 要么就应该把它分配给 满足桥长范围约束条件下(在[c-b,d-a]范围内) 可容纳最长桥长(d-a)最小 的那个位置 那么用堆维护就好了
代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<set>
#include<vector>
#include<queue>
#include<cstdlib>
#include<ctime>
#include<cmath>
using namespace std;
typedef long long LL;
#define N 200010
int n, m, cnt;
int ans[N];
struct bridge {
LL l;
int id;
bool operator<(const bridge ff) const {
return l < ff.l;
}
} b[N];
struct section {
LL l, r, id;
bool operator<(const section ff) const {
return l < ff.l;
}
} s[N];
struct node {
int id;
LL r;
node() {
}
node(LL r, int id) {
this->r = r;
this->id = id;
}
bool operator<(const node ff) const {
return r > ff.r;
}
};
priority_queue<node> qu;
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) scanf("%I64d%I64d", &s[i].l, &s[i].r);
for (int i = 1; i < n; i++) {
LL tmpl, tmpr;
tmpr = s[i + 1].r - s[i].l;
tmpl = s[i + 1].l - s[i].r;
s[i].l = tmpl;
s[i].r = tmpr;
s[i].id = i;
}
sort(s + 1, s + n);
for (int i = 1; i <= m; i++) {
scanf("%I64d", &b[i].l);
b[i].id = i;
}
sort(b + 1, b + m + 1);
int idx = 1;
for (int i = 1; i <= m; i++) {
while (idx < n && s[idx].l <= b[i].l) {
qu.push(node(s[idx].r, s[idx].id));
idx++;
}
if (qu.empty()) continue;
node x = qu.top();
qu.pop();
if (x.r < b[i].l) break;
cnt++;
ans[x.id] = b[i].id;
}
if (cnt == n - 1) {
puts("Yes");
printf("%d", ans[1]);
for (int i = 2; i < n; i++) printf(" %d", ans[i]);
puts("");
} else puts("No");
return 0;
}
C题:
n*n(10^9)的格子 按/方向对角线划开 保留左上部分 m(2*10^5)个询问 每次在/对角线上选一点 向左或向上延伸到最近的空格 并将路线上的格子变成空格 输出变成空格的格子数
思路:
很明显先离散化 我们将横向延伸和纵向延伸分开讨论
由于只有向左和向上两个方向 因此对于向左延伸 只需记录当前y坐标下 最大空格的x是多少 用x做差就是输出值 然后更新对应x区间在横向延伸上的y值记录 向上延伸同理
那么只需要建横纵两棵线段树 单点查询 区间修改 就好了
代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<set>
#include<vector>
#include<queue>
#include<cstdlib>
#include<ctime>
#include<cmath>
using namespace std;
typedef long long LL;
#define N 200010
#define L(x) (x<<1)
#define R(x) ((x<<1)|1)
#define MID(x,y) ((x+y)>>1)
int n, m;
struct ops {
int x, y, to;
} op[N];
int x[N], y[N], vis[N];
struct tree {
int l, r, lazy, near;
} f[2][N << 2];
void init(int l, int r, int i) {
for (int j = 0; j < 2; j++) {
f[j][i].l = l;
f[j][i].r = r;
f[j][i].near = f[j][i].lazy = 0;
}
if (l == r) return;
int mid = MID(l, r);
init(l, mid, L(i));
init(mid + 1, r, R(i));
}
void down(int i, int j) {
if (f[j][i].lazy) {
f[j][L(i)].lazy = max(f[j][L(i)].lazy, f[j][i].lazy);
f[j][L(i)].near = max(f[j][L(i)].near, f[j][i].lazy);
f[j][R(i)].lazy = max(f[j][R(i)].lazy, f[j][i].lazy);
f[j][R(i)].near = max(f[j][R(i)].near, f[j][i].lazy);
f[j][i].lazy = 0;
}
}
void update(int l, int r, int i, int j, int key) {
if (f[j][i].l == l && f[j][i].r == r) {
f[j][i].near = max(f[j][i].near, key);
f[j][i].lazy = max(f[j][i].lazy, key);
return;
}
down(i, j);
int mid = MID(f[j][i].l, f[j][i].r);
if (r <= mid) update(l, r, L(i), j, key);
else if (l > mid) update(l, r, R(i), j, key);
else {
update(l, mid, L(i), j, key);
update(mid + 1, r, R(i), j, key);
}
}
int query(int p, int i, int j) {
if (f[j][i].l == f[j][i].r) return f[j][i].near;
down(i, j);
int mid = MID(f[j][i].l, f[j][i].r);
if (p <= mid) return query(p, L(i), j);
else return query(p, R(i), j);
}
int main() {
char UL[5];
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; i++) {
scanf("%d%d%s", &op[i].x, &op[i].y, UL);
x[i] = op[i].x;
y[i] = op[i].y;
if (UL[0] == 'U') op[i].to = 0;
else op[i].to = 1;
}
sort(x + 1, x + m + 1);
sort(y + 1, y + m + 1);
for (int i = 1; i <= m; i++) {
op[i].x = lower_bound(x + 1, x + m + 1, op[i].x) - x;
op[i].y = lower_bound(y + 1, y + m + 1, op[i].y) - y;
}
init(1, m, 1);
for (int i = 1; i <= m; i++) {
if (!vis[op[i].x]) {
int to, tmpx, tmpy, ans;
to = op[i].to;
tmpx = (to) ? op[i].y : op[i].x;
tmpy = (to) ? op[i].x : op[i].y;
ans = query(tmpx, 1, to);
printf("%d\n", (to) ? (x[tmpy] - x[ans]) : (y[tmpy] - y[ans]));
vis[op[i].x] = 1;
update(ans + 1, tmpy, 1, to^1, tmpx);
} else printf("0\n");
}
return 0;
}
D题:
数轴上有n(2*10^5)个钉子 m(2*10^5)次询问 每次询问在某个钉子上用l长的绳子挂一重物并向右推重物 绳子开始旋转并且可能会碰到新的钉子 然后就绕新的钉子旋转 直到绳子绕一个钉子不停旋转为止 问 这个钉子的标号是几
思路:
除了模拟过程以外并不能想到什么好方法= = 当然模拟的过程中每次找新的钉子要二分去找 那么复杂度看似O(mnlogn)
但是随便想一下就知道 绝对不可能这么坏 旋转中心变换的区间越来越小 期望情况下每次能减少大约一半的钉子数 那么效率比O(mlognlogn)差一些是完全可以接受的 我写了一下 还是TLE在16组数据…
因为这里需要一个小优化 因为考虑到绳子很长 所以旋转中心很可能经常在x,y两个钉子上不停地相互交换 因此如果现在的旋转中心是y 上一次是x 下一次又要到x去 那么我们就计算绕圈圈的次数 这样可以快速缩小旋转中心变换的区间
代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<set>
#include<vector>
#include<queue>
#include<cstdlib>
#include<ctime>
#include<cmath>
using namespace std;
typedef long long LL;
#define N 200010
int n, m;
int x[N], y[N];
struct peg {
int x, id;
bool operator<(const peg ff) const {
return x < ff.x;
}
} tmp[N];
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) {
scanf("%d", &x[i]);
tmp[i].x = x[i];
tmp[i].id = i;
}
sort(x + 1, x + n + 1);
sort(tmp + 1, tmp + n + 1);
for (int i = 1; i <= n; i++)
y[tmp[i].id] = i;
while (m--) {
int i, l, to = 1, cnt = 0, last = -1;
scanf("%d%d", &i, &l);
i = y[i];
while (true) {
if (cnt == 2) {
printf("%d\n", tmp[i].id);
break;
}
if (to) {
if (i + 1 > n || x[i + 1] - x[i] > l) {
cnt++;
to ^= 1;
continue;
}
cnt = 0;
int pos = x[i] + l;
int idx = upper_bound(x + 1, x + n + 1, pos) - x - 1;
if (idx == last) {
if (l / (x[idx] - x[i]) % 2) {
l %= x[idx] - x[i];
last = i;
i = idx;
to ^= 1;
} else {
l %= x[idx] - x[i];
}
} else {
l -= x[idx] - x[i];
last = i;
i = idx;
}
} else {
if (i - 1 < 1 || x[i] - x[i - 1] > l) {
cnt++;
to ^= 1;
continue;
}
cnt = 0;
int pos = x[i] - l;
int idx = lower_bound(x + 1, x + n + 1, pos) - x;
if (idx == last) {
if (l / (x[i] - x[idx]) % 2) {
l %= x[i] - x[idx];
last = i;
i = idx;
to ^= 1;
} else {
l %= x[i] - x[idx];
}
} else {
l -= x[i] - x[idx];
last = i;
i = idx;
}
}
to ^= 1;
}
}
return 0;
}
PS:
博客沉寂了很久了 终于我又来刷题了~~~
上面看不懂的可以留言问我(毕竟语言表述能力有限= = 感觉自己写好差……)