A 小A的树
题目大意 : 在一颗树中求所有点对的距离中最大的k个
-
线段树维护直径可以在nlogn或nlogn2的时间内在一个区间内的数中找出离区间外一点距离最远的点
-
于是可以把点对分成n类,第i类的是i与i之前的匹配,放到优先队列里找最远的点对,然后就可以把i点匹配的区间分成两半,然后再放进队列里
Code
Show Code
#include <queue>
#include <cstdio>
#define ls (rt << 1)
#define rs (rt << 1 | 1)
using namespace std;
typedef long long ll;
const int N = 2e5 + 5;
int read(int x = 0, int f = 1, char c = getchar()) {
for (; c < '0' || c > '9'; c = getchar()) if (c == '-') f = -1;
for (; c >='0' && c <='9'; c = getchar()) x = x * 10 + c - '0';
return x * f;
}
struct Edge {
int n, t, d;
}e[N*2];
int h[N], edc;
void Add(int x, int y, int z) {
e[++edc] = (Edge) {h[x], y, z}; h[x] = edc;
}
int n, k, dep[N], fa[N], son[N], sz[N], tp[N];
ll d[N];
void Dfs(int x) {
dep[x] = dep[fa[x]] + 1; sz[x] = 1;
for (int i = h[x], y; i; i = e[i].n) {
if ((y = e[i].t) == fa[x]) continue;
fa[y] = x; d[y] = d[x] + e[i].d;
Dfs(y); sz[x] += sz[y];
if (sz[son[x]] < sz[y]) son[x] = y;
}
}
void Dfs(int x, int top) {
tp[x] = top;
if (son[x]) Dfs(son[x], top);
for (int i = h[x], y; i; i = e[i].n)
if ((y = e[i].t) != fa[x] && y != son[x]) Dfs(y, y);
}
ll Dis(int x, int y) {
ll ans = d[x] + d[y];
while (tp[x] != tp[y])
dep[tp[x]] > dep[tp[y]] ? x = fa[tp[x]] : y = fa[tp[y]];
return ans - 2 * d[dep[x] < dep[y] ? x : y];
}
struct Tree {
int x, y; ll d;
}t[N*4];
Tree operator + (const Tree &a, const Tree &b) {
Tree c = a.d > b.d ? a : b;
int x[] = {a.x, a.y}, y[] = {b.x, b.y};
for (int i = 0; i < 2; ++i) {
for (int j = 0; j < 2; ++j) {
ll d = Dis(x[i], y[j]);
if (d > c.d) c = (Tree) {x[i], y[j], d};
}
}
return c;
}
void Build(int rt, int l, int r) {
if (l == r) return t[rt] = (Tree) {l, l}, void();
int mid = l + r >> 1;
Build(ls, l, mid); Build(rs, mid+1, r);
t[rt] = t[ls] + t[rs];
}
Tree Ask(int rt, int l, int r, int x, int y) {
if (x <= l && r <= y) return t[rt];
int mid = l + r >> 1;
if (y <= mid) return Ask(ls, l, mid, x, y);
if (x > mid) return Ask(rs, mid+1, r, x, y);
return Ask(ls, l, mid, x, y) + Ask(rs, mid+1, r, x, y);
}
struct Node {
int l, r, x, mid; ll d;
Node() {}
Node(int a, int b, int c) {
l = a; r = b; x = c;
Tree t = Ask(1, 1, n, l, r);
ll d1 = Dis(x, t.x), d2 = Dis(x, t.y);
if (d1 > d2) mid = t.x, d = d1;
else mid = t.y, d = d2;
}
};
bool operator < (const Node &a, const Node &b) {
return a.d < b.d;
}
priority_queue<Node> q;
int main() {
freopen("tree.in", "r", stdin);
freopen("tree.out", "w", stdout);
n = read(); k = read();
for (int i = 1; i < n; ++i) {
int x = read(), y = read(), z = read();
Add(x, y, z); Add(y, x, z);
}
Dfs(1); Dfs(1, 1); Build(1, 1, n);
for (int i = 2; i <= n; ++i)
q.push(Node(1, i-1, i));
while (k--) {
Node a = q.top(); q.pop();
printf("%lld
", a.d);
int l = a.l, r = a.r, mid = a.mid, x = a.x;
if (l < mid) q.push(Node(l, mid-1, x));
if (mid < r) q.push(Node(mid+1, r, x));
}
return 0;
}
B 小B的序列
题目大意 : 区间取与,区间取或,区间查最大值
-
线段树维护区间与和,或和,区间最大值,修改的时候,如果这个区间与和和或和改变了一样,就区间加变化值
-
正确性好像挺显然,比如取与的时候,会把1变成0,如果这个区间里与和修改的地方和或和修改的地方一样,那么一定所有数都会改变一样的只
-
时间复杂度的话不会证明,大概就是修改的时候会将区间变得更平?
Code
Show Code
#include <cstdio>
#include <algorithm>
#define ls (rt << 1)
#define rs (rt << 1 | 1)
using namespace std;
const int N = 2e5 + 5;
int read(int x = 0, int f = 1, char c = getchar()) {
for (; c < '0' || c > '9'; c = getchar()) if (c == '-') f = -1;
for (; c >='0' && c <='9'; c = getchar()) x = x * 10 + c - '0';
return x * f;
}
int n, m, a[N];
struct Tree {
int m, a, o, tag;
}t[N*4];
void Pushup(int rt) {
t[rt].a = t[ls].a & t[rs].a;
t[rt].o = t[ls].o | t[rs].o;
t[rt].m = max(t[ls].m, t[rs].m);
}
void Update(int rt, int w) {
t[rt].m += w; t[rt].a += w; t[rt].o += w; t[rt].tag += w;
}
void Pushdown(int rt) {
if (!t[rt].tag) return;
Update(ls, t[rt].tag);
Update(rs, t[rt].tag);
t[rt].tag = 0;
}
void Build(int rt, int l, int r) {
if (l == r) return t[rt] = (Tree) {a[l], a[l], a[l]}, void();
int mid = l + r >> 1;
Build(ls, l, mid); Build(rs, mid+1, r);
Pushup(rt);
}
void And(int rt, int l, int r, int x, int y, int w) {
if (x <= l && r <= y && (w & t[rt].a) - t[rt].a == (w & t[rt].o) - t[rt].o)
return Update(rt, (w & t[rt].a) - t[rt].a);
int mid = l + r >> 1; Pushdown(rt);
if (x <= mid) And(ls, l, mid, x, y, w);
if (y > mid) And(rs, mid+1, r, x, y, w);
Pushup(rt);
}
void Or(int rt, int l, int r, int x, int y, int w) {
if (x <= l && r <= y && (w | t[rt].a) - t[rt].a == (w | t[rt].o) - t[rt].o)
return Update(rt, (w | t[rt].a) - t[rt].a);
int mid = l + r >> 1; Pushdown(rt);
if (x <= mid) Or(ls, l, mid, x, y, w);
if (y > mid) Or(rs, mid+1, r, x, y, w);
Pushup(rt);
}
int Ask(int rt, int l, int r, int x, int y) {
if (x <= l && r <= y) return t[rt].m;
int mid = l + r >> 1, ans = 0; Pushdown(rt);
if (x <= mid) ans = Ask(ls, l, mid, x, y);
if (y > mid) ans = max(ans, Ask(rs, mid+1, r, x, y));
return ans;
}
int main() {
freopen("sequence.in", "r", stdin);
freopen("sequence.out", "w", stdout);
n = read(); m = read();
for (int i = 1; i <= n; ++i)
a[i] = read();
Build(1, 1, n);
while (m--) {
int od = read(), l = read(), r = read();
if (od == 1) And(1, 1, n, l, r, read());
else if (od == 2) Or(1, 1, n, l, r, read());
else printf("%d
", Ask(1, 1, n, l, r));
}
return 0;
}
C 小C的利是 (Unaccepted)
题目大意 : 在n×n的阵中选n个不为-1的数,每行每列最多选1个数,问多少种选法可以使得选的数的和与k同余
- 正解是行列式,但数据挺水,搜索到一定次数输出No就能过
Code
Show Code