P4180 [BJWC2010]严格次小生成树
可以证明,有至少一个(严格)次小生成树,和最小生成树之间只有一条边的差异。
先求出该图的最小生成树。
对于每一条边
树剖或倍增求出路径上的最大、次大值即可。
#include <bits/stdc++.h>
#define d(x) cerr << #x << " => " << x << endl
#define dd(x) cerr << #x << endl
#define D cerr << "Passed [" << __FUNCTION__ << "] on line " << __LINE__ << endl
#define ms(x) memset(x, 0, sizeof x)
#define int long long
using namespace std;
int rd() {
int f = 1, val = 0; char ch = getchar();
while(!isdigit(ch)) f = (ch == '-' ? -1 : 1), ch = getchar();
while(isdigit(ch)) val = val * 10 + ch - '0', ch = getchar();
return f * val;
}
void file() {
freopen("in.in", "r", stdin);
//freopen("out.out", "w+", stdout);
}
const int N = 1e5 + 5, M = 3e5 + 5;
const int inf = 0x3f3f3f3f3f3f3f3f;
class UnionSet {
public :
static const int N = ::N;
int fa[N];
UnionSet() {
for(int i = 1; i < N; i++) fa[i] = i;
}
int leader(int x) {
return fa[x] == x ? x : fa[x] = leader(fa[x]);
}
void merge(int x, int y) {
int fax = leader(x), fay = leader(y);
if(fax == fay) return ;
fa[fax] = fay;
}
} us;
struct Prepare {
int u, v, w;
bool operator<(const Prepare& a) const {
return w < a.w;
}
} e[M << 1];
int used[M];
struct Edge {
int to, nxt, wei;
} Edge[M << 1];
int head[N], cnt;
void addEdge(int u, int v, int w) {
Edge[++cnt].to = v;
Edge[cnt].nxt = head[u];
Edge[cnt].wei = w;
head[u] = cnt;
}
int dep[N], fa[N][20], maxx[N][20], cmax[N][20], belongTo[N];
void dfs1(int u, int d, int depth) {
dep[u] = depth; fa[u][0] = d;
for(int i = 1; i <= 19; i++) fa[u][i] = fa[fa[u][i - 1]][i - 1];
for(int i = head[u]; i; i = Edge[i].nxt) {
const int v = Edge[i].to;
if(v == d) continue;
dfs1(v, u, depth + 1);
belongTo[v] = Edge[i].wei;
}
}
typedef pair<int, int> pi;
pi merge(int a, int b, int c, int d) {
vector<int> res {a, b, c, d};
sort(res.begin(), res.end());
res.erase(unique(res.begin(), res.end()), res.end());
return make_pair(res[res.size() - 1], res.size() == 1 ? 0 : res[res.size() - 2]);
}
pi merge(int a, int b, int c, int d, int e, int f) {
pi res = merge(a, b, c, d);
return merge(res.first, res.second, e, f);
}
pi merge(int u, int v, int t) {
return merge(maxx[u][t], cmax[u][t], maxx[v][t], cmax[v][t]);
}
void dfs2(int u, int d) {
maxx[u][0] = belongTo[u];
for(int i = 1; i <= 19; i++) {
pi res = merge(u, fa[u][i - 1], i - 1);
maxx[u][i] = res.first; cmax[u][i] = res.second;
}
for(int i = head[u]; i; i = Edge[i].nxt) {
const int v = Edge[i].to;
if(v == d) continue;
dfs2(v, u);
}
}
pi judge(int x, int y) {
pi res = make_pair(0, 0);
if(dep[x] < dep[y]) swap(x, y);
int dis = dep[x] - dep[y];
for(int i = 19; ~i; i--) if(dis & (1 << i)) {
res = merge(res.first, res.second, maxx[x][i], cmax[x][i]);
x = fa[x][i];
}
if(x == y) return res;
for(int i = 19; ~i; i--) if(fa[x][i] != fa[y][i]) {
res = merge(res.first, res.second, maxx[x][i], maxx[y][i], cmax[x][i], cmax[y][i]);
x = fa[x][i]; y = fa[y][i];
}
res = merge(res.first, res.second, maxx[x][0], cmax[x][0], maxx[y][0], cmax[y][0]);
return res;
}
signed main() {
#ifdef LOCAL
file();
#endif
memset(cmax, 0xaf, sizeof cmax);
memset(maxx, 0xaf, sizeof maxx);
int n = rd(), m = rd();
for(int i = 1; i <= m; i++) {
e[i].u = rd(), e[i].v = rd(), e[i].w = rd();
}
sort(e + 1, e + m + 1);
int sum = 0;
for(int i = 1; i <= m; i++) {
if(us.leader(e[i].u) == us.leader(e[i].v)) continue;
addEdge(e[i].u, e[i].v, e[i].w);
addEdge(e[i].v, e[i].u, e[i].w);
used[i] = true; sum += e[i].w;
us.merge(e[i].u, e[i].v);
}
dfs1(1, 1, 1);
dfs2(1, 1);
int ans = inf;
for(int i = 1; i <= m; i++) {
if(used[i]) continue;
if(e[i].u == e[i].v) continue;
pi res = judge(e[i].u, e[i].v);
int r = res.first == e[i].w ? res.second : res.first;
ans = min(ans, sum - r + e[i].w);
}
cout << ans << endl;
return 0;
}
P3787 冰精冻西瓜
本题区间修改,单点查询。
考虑到我们不会区间修改一些不同的量,所以只能在单点查询上下文章。
考虑我们能不能区间修改相同的量。
设
注意到乘法具有可逆操作。如果我们统一地按照改动在树根上处理。就可以区间修改相同的值了。
对于边权为
#include <bits/stdc++.h>
#define d(x) cerr << #x << " => " << x << endl
#define dd(x) cerr << #x << endl
#define D cerr << "Passed [" << __FUNCTION__ << "] on line " << __LINE__ << endl
#define ms(x) memset(x, 0, sizeof x)
#define double long double
using namespace std;
int rd() {
int f = 1, val = 0; char ch = getchar();
while(!isdigit(ch)) f = (ch == '-' ? -1 : 1), ch = getchar();
while(isdigit(ch)) val = val * 10 + ch - '0', ch = getchar();
return f * val;
}
void file() {
freopen("in.in", "r", stdin);
//freopen("out.out", "w+", stdout);
}
const int N = 1e5 + 5, M = 1e5 + 5;
class SegmentTree {
public:
#define MID int mid = (l + r) >> 1
#define lson l, mid, x << 1
#define rson mid + 1, r, x << 1 | 1
static const int N = 1e6 + 6;
double val[N << 2], lzy[N << 2];
void build(int l, int r, int x) {
if(l == r) {
cin >> val[x];
return ;
}
MID; build(lson); build(rson);
push_up(x);
}
void push_up(int x) {
val[x] = val[x << 1] + val[x << 1 | 1];
}
void push_down(int l, int r, int x) {
if(lzy[x]) {
MID;
val[x << 1] += lzy[x] * (mid - l + 1);
val[x << 1 | 1] += lzy[x] * (r - mid);
lzy[x << 1] += lzy[x];
lzy[x << 1 | 1] += lzy[x];
lzy[x] = 0;
}
}
void add(int l, int r, int x, int p, int q, double v) {
if(p <= l && r <= q) {
val[x] += v * (r - l + 1);
lzy[x] += v;
return ;
}
MID; push_down(l, r, x);
if(p <= mid) add(lson, p, q, v);
if(q > mid) add(rson, p, q, v);
push_up(x);
}
double query(int l, int r, int x, int p, int q) {
if(p <= l && r <= q) {
return val[x];
}
MID; push_down(l, r, x); double res = 0;
if(p <= mid) res += query(lson, p, q);
if(q > mid) res += query(rson, p, q);
return res;
}
} st;
const double eps = 0.00000001;
template<typename T>
T myabs(T x) {
return x < 0 ? -x : x;
}
struct Prepare {
int u, v;
double w;
} e[N << 1];
class Graph {
public :
static const int N = ::N, M = ::M;
struct Edge {
int to, nxt;
double wei;
} Edge[M << 1];
int head[N], cnt;
void addEdge(int u, int v, double w) {
Edge[++cnt].to = v;
Edge[cnt].nxt = head[u];
Edge[cnt].wei = w;
head[u] = cnt;
}
} g, gn;
vector<int> root;
void dfs1(int u, int d) {
for(int i = g.head[u]; i; i = g.Edge[i].nxt) {
const int v = g.Edge[i].to;
if(v == d) continue;
// printf("%d %d %.8lf\n", u, v, g.Edge[i].wei);
if(myabs(g.Edge[i].wei) <= eps) {
root.push_back(v);
} else {
gn.addEdge(u, v, g.Edge[i].wei);
gn.addEdge(v, u, g.Edge[i].wei);
}
dfs1(v, u);
}
}
int id[N], sz[N], idx;
double tot[N], c[N];
void dfs2(int u, int d) {
id[u] = ++idx; sz[u] = 1;
for(int i = gn.head[u]; i; i = gn.Edge[i].nxt) {
const int &v = gn.Edge[i].to;
if(v == d) continue;
tot[v] = tot[u] * gn.Edge[i].wei;
dfs2(v, u);
sz[u] += sz[v];
}
}
signed main() {
#ifdef LOCAL
file();
#endif
int n = rd();
for(int i = 1; i < n; i++) {
int u = rd(), v = rd(); double w;
scanf("%Lf", &w);
e[i].u = u, e[i].v = v, e[i].w = w;
g.addEdge(u, v, w); g.addEdge(v, u, w);
}
root.push_back(1);
dfs1(1, 1);
// for(auto it : root) cout << it << " "; cout << endl;
for(auto it : root) {
tot[it] = 1;
dfs2(it, it);
}
int m = rd();
for(int i = 1; i <= m; i++) {
int opt = rd();
if(opt == 1) {
int pos = rd(); double v; scanf("%Lf", &v);
st.add(1, n, 1, id[pos], id[pos] + sz[pos] - 1, v / tot[pos]);
} else if(opt == 9) {
int x = rd();
printf("%.8Lf\n", st.query(1, n, 1, id[x], id[x]) * tot[x]);
}
}
return 0;
}
P1712 [NOI2016] 区间
首先离散化不会影响答案。故离散化。
将区间按照长度排序后,应用双指针的思想。
发现我们还要维护是否有区间长度
「接着我们又注意到题目要求的答案跟“最大”,“最小”有关,所以我们就会联想到单调性。」
#include <bits/stdc++.h>
#define d(x) cerr << #x << " => " << x << endl
#define dd(x) cerr << #x << endl
#define D cerr << "Passed [" << __FUNCTION__ << "] on line " << __LINE__ << endl
#define ms(x) memset(x, 0, sizeof x)
using namespace std;
int rd() {
int f = 1, val = 0; char ch = getchar();
while(!isdigit(ch)) f = (ch == '-' ? -1 : 1), ch = getchar();
while(isdigit(ch)) val = val * 10 + ch - '0', ch = getchar();
return f * val;
}
void file() {
freopen("in.in", "r", stdin);
//freopen("out.out", "w+", stdout);
}
const int N = 1e6 + 233;
struct Lens {
int l, r, len;
bool operator<(const Lens& a) const {
return len < a.len;
}
} a[N];
vector<int> b;
int getRnk(int x) {
return lower_bound(b.begin(), b.end(), x) - b.begin() + 1;
}
class SegmentTree {
public :
#define MID int mid = (l + r) >> 1
#define lson l, mid, x << 1
#define rson mid + 1, r, x << 1 | 1
static const int N = ::N;
int maxx[N << 2], sum[N << 2], add[N << 2];
void modify_add(int l, int r, int x, int p, int q, int v) {
if(p <= l && r <= q) {
sum[x] = (r - l + 1) * v;
maxx[x] += v;
add[x] += v;
return ;
}
MID; push_down(l, r, x);
if(p <= mid) modify_add(lson, p, q, v);
if(q > mid) modify_add(rson, p, q, v);
push_up(x);
}
void push_up(int x) {
sum[x] = sum[x << 1] + sum[x << 1 | 1];
maxx[x] = max(maxx[x << 1], maxx[x << 1 | 1]);
}
void push_down(int l, int r, int x) {
if(!add[x]) return ;
MID;
sum[x << 1] += (mid - l + 1) * add[x];
sum[x << 1 | 1] += (r - mid) * add[x];
add[x << 1] += add[x];
add[x << 1 | 1] += add[x];
maxx[x << 1] += add[x];
maxx[x << 1 | 1] += add[x];
add[x] = 0;
}
} st;
signed main() {
#ifdef LOCAL
file();
#endif
int n = rd(), m = rd();
for(int i = 1; i <= n; i++) {
a[i].l = rd(), a[i].r = rd();
b.push_back(a[i].l); b.push_back(a[i].r);
a[i].len = a[i].r - a[i].l + 1;
}
sort(b.begin(), b.end());
for(int i = 1; i <= n; i++) {
a[i].l = getRnk(a[i].l);
a[i].r = getRnk(a[i].r);
}
sort(a + 1, a + n + 1);
int ans = 0x3f3f3f3f, l = 0, r = 0;
while(r < n) {
while(r < n && st.maxx[1] < m) {
++r;
st.modify_add(1, 2 * n + 5, 1, a[r].l, a[r].r, 1);
}
if(st.maxx[1] == m) {
while(l < r && st.maxx[1] == m) {
++l;
st.modify_add(1, 2 * n + 5, 1, a[l].l, a[l].r, -1);
}
ans = min(ans, a[r].len - a[l].len);
}
}
if(ans == 0x3f3f3f3f) puts("-1");
else printf("%d\n", ans);
return 0;
}
P3616 富金森林公园
考虑一个定理。对于一个相邻数字两两不相等的数列,其
证明考虑数学归纳法。
考虑扩充定义使得该命题对于相邻两数字相等的情况也成立。
如果将
我们发现上述命题同样成立。
因此本题可以维护可见的山峰和山谷。
#include <bits/stdc++.h>
#define d(x) cerr << #x << " => " << x << endl
#define dd(x) cerr << #x << endl
#define D cerr << "Passed [" << __FUNCTION__ << "] on line " << __LINE__ << endl
#define ms(x) memset(x, 0, sizeof x)
using namespace std;
int rd() {
int f = 1, val = 0; char ch = getchar();
while(!isdigit(ch)) f = (ch == '-' ? -1 : 1), ch = getchar();
while(isdigit(ch)) val = val * 10 + ch - '0', ch = getchar();
return f * val;
}
void file() {
freopen("in.in", "r", stdin);
//freopen("out.out", "w+", stdout);
}
vector<int> b;
int getRnk(int x) {
return lower_bound(b.begin(), b.end(), x) - b.begin() + 1;
}
const int N = 4e5 + 5, inf = 0x3f3f3f3f;
class TreeArray {
public :
static const int N = ::N;
int sum[N];
int lowbit(int x) { return x & -x; }
void add(int u, int v) { u++; for(int i = u; i < N; i += lowbit(i)) sum[i] += v; }
int query(int u) { u++; int res = 0; for(int i = u; i; i -= lowbit(i)) res += sum[i]; return res; }
} peak, valley;
struct Question {
int opt, x, y;
} Ques[N];
int a[N];
int totpeak = 0, totvalley = 0;
void update(int i, int v) {
if(a[i - 1] <= a[i] && a[i] > a[i + 1]) peak.add(a[i], v), totpeak += v;
if(a[i - 1] > a[i] && a[i] <= a[i + 1]) valley.add(a[i], v), totvalley += v;
}
signed main() {
#ifdef LOCAL
file();
#endif
int n = rd(), m = rd();
for(int i = 1; i <= n; i++) {
a[i] = rd();
b.push_back(a[i]);
}
for(int i = 1; i <= m; i++) {
Ques[i].opt = rd();
Ques[i].x = rd();
if(Ques[i].opt == 1) {
b.push_back(Ques[i].x);
} else if (Ques[i].opt == 2) {
Ques[i].y = rd();
b.push_back(Ques[i].y);
}
}
sort(b.begin(), b.end());
for(int i = 1; i <= n; i++) {
a[i] = getRnk(a[i]);
}
for(int i = 1; i <= m; i++) {
if(Ques[i].opt == 1) {
Ques[i].x = getRnk(Ques[i].x);
} else if(Ques[i].opt == 2) {
Ques[i].y = getRnk(Ques[i].y);
}
}
a[0] = -inf; a[n + 1] = -inf;
for(int i = 1; i <= n; i++) {
update(i, 1);
}
for(int j = 1; j <= m; j++) {
if(Ques[j].opt == 1) {
int x = Ques[j].x;
printf("%d\n", totpeak - peak.query(x - 1) - (totvalley - valley.query(x - 1)));
} else if(Ques[j].opt == 2) {
int i = Ques[j].x, v = Ques[j].y;
update(i, -1);
if(i != 1) update(i - 1, -1);
if(i != n) update(i + 1, -1);
a[i] = v;
update(i, 1);
if(i != 1) update(i - 1, 1);
if(i != n) update(i + 1, 1);
}
}
return 0;
}
P3313 [SDOI2014]旅行
不想写。
树剖然后暴力开线段树就行。动态开点。
P4216 [SCOI2015]情报传递
不想写。
将每一个开始搜集情报的点打上标记(即开始时间)
查询对于
套路性的主席树维护即可。
此处评论已关闭