@
头文件
#pragma comment(linker, "/STACK:102400000,102400000")
//#include<bits/stdc++.h>
#include <ctime>
#include <iostream>
#include <assert.h>
#include <vector>
#include <queue>
#include <cstdio>
#include <algorithm>
#include <cstring>
#define fi first
#define se second
#define endl '\n'
#define o2(x) (x)*(x)
#define BASE_MAX 31
#define mk make_pair
#define eb push_back
#define SZ(x) ((int)(x).size())
#define all(x) (x).begin(), (x).end()
#define clr(a, b) memset((a),(b),sizeof((a)))
#define iis std::ios::sync_with_stdio(false); cin.tie(0)
#define my_unique(x) sort(all(x)),x.erase(unique(all(x)),x.end())
using namespace std;
#pragma optimize("-O3")
typedef long long LL;
typedef unsigned long long uLL;
typedef pair<int, int> pii;
inline LL read() {
LL x = 0;int f = 0;
char ch = getchar();
while (ch < '0' || ch > '9') f |= (ch == '-'), ch = getchar();
while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
return x = f ? -x : x;
}
inline void write(LL x, bool f) {
if (x == 0) {putchar('0'); if(f)putchar('\n');else putchar(' ');return;}
if (x < 0) {putchar('-');x = -x;}
static char s[23];
int l = 0;
while (x != 0)s[l++] = x % 10 + 48, x /= 10;
while (l)putchar(s[--l]);
if(f)putchar('\n');else putchar(' ');
}
int lowbit(int x) { return x & (-x); }
template<class T>T big(const T &a1, const T &a2) { return a1 > a2 ? a1 : a2; }
template<class T>T sml(const T &a1, const T &a2) { return a1 < a2 ? a1 : a2; }
template<typename T, typename ...R>T big(const T &f, const R &...r) { return big(f, big(r...)); }
template<typename T, typename ...R>T sml(const T &f, const R &...r) { return sml(f, sml(r...)); }
void debug_out() { cerr << '\n'; }
template<typename T, typename ...R>void debug_out(const T &f, const R &...r) {cerr << f << " ";debug_out(r...);}
#define debug(...) cerr << "[" << #__VA_ARGS__ << "]: ", debug_out(__VA_ARGS__);
const LL INFLL = 0x3f3f3f3f3f3f3f3fLL;
const int HMOD[] = {1000000009, 1004535809};
const LL BASE[] = {1572872831, 1971536491};
const int mod = 1e7 + 7;
const int MOD = 1e7 + 7;//998244353
const int INF = 0x3f3f3f3f;
#ifndef ONLINE_JUDGE
freopen("/home/cwolf9/CLionProjects/ccc/in.txt", "r", stdin);
//freopen("/home/cwolf9/CLionProjects/ccc/out.txt", "w", stdout);
#endif
线性基
线性基交:牛客2019多校第四场B题
struct Base {
int b[BASE_MAX + 1];
int& operator [](int x) {
return b[x];
}
int operator [](int x)const {
return b[x];
}
void clear(int f) {
if(f == 0) memset(b, 0, sizeof(int)*(BASE_MAX+1));
else {
for(int i = 0; i <= BASE_MAX; ++i) b[i] = (1<<i);
}
}
bool check(int x) {
for(int i = BASE_MAX; i >= 0; --i) {
if(x & (1 << i)) x ^= b[i];
}
return (x == 0);
}
void out() {
for(int i = 0; i <= BASE_MAX; ++i) printf("%d ", b[i]);
printf("\n");
}
}bs[MXN];
bool insert(int x, int *bs) {
for(int j = BASE_MAX; j >= 0; --j) {
if(!(x >> j)) continue;
if(bs[j]) x ^= bs[j];
else {
bs[j] = x;
for(int k = j-1; k >= 0; --k) if(bs[k]&&(bs[j]&(1LL<<k))) bs[j]^=bs[k];
for(int k = j+1; k <= BASE_MAX; ++k) if(bs[k]&(1LL<<j)) bs[k]^=bs[j];
return true;
}
}
return false;
}
Base merge(const Base&a, const Base&b) {//600ms
Base c = b, d = b, rt = {};
for(int i = 0; i <= BASE_MAX; ++i) assert(rt[i] == 0);
for (int i = 0, x; i <= BASE_MAX; ++i) {
x = a[i];
if (!x)continue;
int j = i, T = 0;
for (; j >= 0; --j) {
if ((x >> j) & 1)
if (c[j]) { x ^= c[j]; T ^= d[j]; }
else break;
}
if (!x) rt[i] = T;
else { c[j] = x; d[j] = T; }
}
return rt;
}
Base merge1(const Base&a, const Base&b) {//400ms
int cur, d;
Base tot = a, na = a, rt = {};
for(int i = 0; i <= BASE_MAX; ++i) assert(rt[i] == 0);
for(int i = 0; i <= BASE_MAX; ++i) if(b[i]) {
cur = 0, d = b[i];
for(int j = i; j >= 0; --j) if(d >> j & 1) {
if(tot[j]) {
d ^= tot[j], cur ^= na[j];
if(d == 0) { rt[i] = cur; break; }
} else {
tot[j] = d;
na[j] = cur;
break;
}
}
}
return rt;
}
单调栈/单调队列相关
- 单调栈就是用一个栈实现的,可以求左/右边第一个大于/小于他的数字。(笛卡尔树是单调栈升级版)
- 单调队列用一个双端队列实现,可以头指针和尾指针同时右移。可以保证这个区间内最大/小值位于左端点。
- 单调栈:我是新来的,都得让着我
- 单调队列:事已至此,不得不心狠手辣
2019牛客第三场F题
//类似与求区间最大最小值差小于等于q的区间数量
void solve(int len) {
// clr(dq1, 0), clr(dq2, 0);
head1 = tail1 = head2 = tail2 = 0;
int tmp = 0;
for(int i = 1; i <= n; ++i) {
while(head1 < tail1 && Max[i] > Max[dq1[tail1]]) -- tail1;
while(head2 < tail2 && Min[i] < Min[dq2[tail2]]) -- tail2;
dq1[++tail1] = i, dq2[++tail2] = i;
// debug(head1, tail1, head1, tail2)
while(head1 < tail1 && head2 < tail2 && Max[dq1[head1 + 1]] - Min[dq2[head2 + 1]] > q) {
if(dq1[head1+1] < dq2[head2+1]) tmp = dq1[++head1];
else tmp = dq2[++head2];
}
ans = big(ans, (i - tmp) * len);
// debug(len, tmp, i)
}
}
树分治
点分治
裸题:poj1741
分治的复杂度是较为稳定的\(O(nlog(n))\)。
静态点分治还是比较简单吧,按重心分治,每次能让子树大小减少一半以上,所以分治层数至多\(log(n)\)层,这也是他复杂度稳定的一个原因吧。
每递归一层都要重新求一次该子树的重心,因为每次把重新标记为1了,所以各子树间也不会互相有影响。
后面动态点分治也是在此基础上建立的,感觉动态点分治利用了把重心连成一棵高度均衡\(log(n)\)层的树(点分树?),然后使得一些复杂度看似不太对的暴力,成为了可行解。
复杂度:\(O(nlog(n)^2)\)
const int MXN = 2e4 + 7;
const int MXE = 4e4 + 7;
int n, m;
int tn;
struct lp{
int v, nex;
int w;
}cw[MXE];
int tot, head[MXN], dep[MXN], siz[MXN], hvy, hvysiz, vis[MXN];
int stak[MXN];
LL ans;
void add_edge(int a, int b, int c) {
cw[++ tot].v = b, cw[tot].nex = head[a], cw[tot].w = c;
head[a] = tot;
cw[++ tot].v = a, cw[tot].nex = head[b], cw[tot].w = c;
head[b] = tot;
}
void dfs_pre(int u, int ba) {
int mm = 0;
siz[u] = 1;
for(int i = head[u], v; ~i; i = cw[i].nex) {
v = cw[i].v;
if(v == ba || vis[v]) continue;
dfs_pre(v, u);
siz[u] += siz[v];
mm = big(mm, siz[v]);
}
mm = big(mm, tn - siz[u]);
if(hvy == -1 || (mm < hvysiz)) {
hvy = u;
hvysiz = mm;
}
}
void get_dep(int u, int ba) {
stak[++ stak[0]] = dep[u];
for(int i = head[u], v; ~i; i = cw[i].nex) {
v = cw[i].v;
if(v == ba || vis[v]) continue;
dep[v] = dep[u] + cw[i].w;
get_dep(v, u);
}
}
void get_ans(int u, int w, int flag) {
stak[0] = 0;
dep[u] = w;
get_dep(u, u);
sort(stak + 1, stak + 1 + stak[0]);
for(int i = 1, j = stak[0]; i <= stak[0]; ++i) {
while(j > i && stak[i] + stak[j] > m) -- j;
ans += flag * big(0, j - i);
}
// printf("%d :\n", u);
// for(int i = 1; i <= stak[0]; ++i) printf("%d ", stak[i]);
// printf("\n");
}
void dfs_dian(int u, int ba) {
vis[u] = 1;
get_ans(u, 0, 1);
// printf("%d %lld\n", u, ans);
for(int i = head[u], v; ~i; i = cw[i].nex) {
v = cw[i].v;
if(v == ba || vis[v]) continue;
get_ans(v, cw[i].w, -1);
tn = siz[v];
hvy = hvysiz = -1;
dfs_pre(v, u);
dfs_dian(hvy ,0);
}
}
int main() {
while(~scanf("%d%d", &n, &m) && (n + m)) {
for(int i = 1; i <= n; ++i) head[i] = -1, vis[i] = 0;
tot = -1;
for(int i = 1, a, b, c; i < n; ++i) {
scanf("%d%d%d", &a, &b, &c);
add_edge(a, b, c);
}
ans = 0;
tn = n;
hvy = hvysiz = -1;
dfs_pre(1, 0);
dfs_dian(hvy, 0);
printf("%lld\n", ans);
}
return 0;
}
边分治
裸题:poj1741
普通的边分治复杂度极不稳定,参考菊花图。边分治复杂度主要决定因素是点的度数。
解决方法是在不影响求解答案的前提下,重构图。让每个点度数至多为\(3\),又因为你至多会增加\(n\)个点,所以重构图之后边分治复杂度也是比较稳定的,可能常数会比较大吧。
边分治重建树时有两种重建的方法,一种是构建成类线段树结构(高度均衡),一种是每出现两个点就新建一个点。
法1用vector写比较方便,法2可以不用vector实现(法2有个优化就是让原节点连接一个虚节点和原儿子节点,而不是直接连接一个虚节点)。
老年poj上面法1:500ms,法2:300ms。点分治:200ms。
感觉我边分治写法常数应该巨大无比才对啊,为啥还能不到300ms的。。
复杂度:\(O(nlog(n)^2)\)
//类线段树重建图 500ms
const int MXN = 3e4 + 7;
const int MXE = 6e4 + 7;
int n, m;
int tn;
struct lp{
int v, nex;
int w, u;
}cw[MXE];
int tot, head[MXN], siz[MXN], hvy, hvysiz, vis[MXE];
int stkl[MXN], stkr[MXN], suml[MXN], sumr[MXN], id[MXN], sdl[MXN], sdr[MXN], sum[MXN];
vector<pii > vs[MXN];
LL ans;
void add_edge(int a, int b, int c) {
cw[++ tot].v = b, cw[tot].nex = head[a], cw[tot].w = c, cw[tot].u = a;
head[a] = tot;
cw[++ tot].v = a, cw[tot].nex = head[b], cw[tot].w = c, cw[tot].u = b;
head[b] = tot;
}
void dfs_pre(int u, int ba, int _tn) {
siz[u] = 1;
for(int i = head[u], v; ~i; i = cw[i].nex) {
v = cw[i].v;
if(v == ba || vis[i] || vis[i^1]) continue;
dfs_pre(v, u, _tn);
siz[u] += siz[v];
if(hvy == -1 || big(hvysiz, _tn - hvysiz) > big(siz[v], _tn - siz[v])) {
hvy = i;
hvysiz = siz[v];
}
}
}
void dfs_dep(int f, int u, int ba, int w) {
if(f) stkl[++stkl[0]] = w, suml[stkl[0]] = id[u];
else stkr[++stkr[0]] = w, sumr[stkr[0]] = id[u];
for(int i = head[u], v; ~i; i = cw[i].nex) {
v = cw[i].v;
if(v == ba || vis[i] || vis[i^1]) continue;
dfs_dep(f, v, u, w + cw[i].w);
}
}
bool cmpl(const int&a, const int&b) {
return stkl[a] < stkl[b];
}
bool cmpr(const int&a, const int&b) {
return stkr[a] < stkr[b];
}
void dfs_ans(int u, int _n) {
if(_n <= 1) return;
hvy = hvysiz = -1;
dfs_pre(u, 0, _n);
// printf("--%d %d %d %d\n", u, hvysiz, cw[hvy].u, cw[hvy].v);
int tmphvy = hvy, tmpsiz = hvysiz;
vis[tmphvy] = vis[tmphvy^1] = 1;
stkl[0] = stkr[0] = 0;
dfs_dep(1, cw[tmphvy].u, -1, 0), dfs_dep(0, cw[tmphvy].v, -1, 0);
for(int i = 1; i <= stkl[0]; ++i) sdl[i] = i;
// for(int i = 1; i <= stkl[0]; ++i) printf("%d ", stkl[i]);
// printf("\n");
// for(int i = 1; i <= stkl[0]; ++i) printf("%d ", suml[i]);
// printf("\n");
for(int i = 1; i <= stkr[0]; ++i) sdr[i] = i;
// for(int i = 1; i <= stkr[0]; ++i) printf("%d ", sumr[i]);
// printf("\n");
sort(sdl + 1, sdl + stkl[0] + 1, cmpl), sort(sdr + 1, sdr + stkr[0] + 1, cmpr);
for(int i = 1; i <= stkr[0]; ++i) sum[i] = sum[i-1] + sumr[sdr[i]];
// for(int i = 1; i <= stkr[0]; ++i) printf("%d ", sum[i]);
// printf("\n");
// for(int i = 1; i <= stkr[0]; ++i) printf("%d ", stkr[sdr[i]]);
// printf("\n");
// sort(stkl + 1, stkl + 1 + stkl[0]), sort(stkr + 1, stkr + 1 + stkr[0]);
for(int i = 1, j = stkr[0]; i <= stkl[0]; ++i) {
// printf("%d ", stkl[sdl[i]]);
while(j >= 1 && stkl[sdl[i]] + cw[tmphvy].w + stkr[sdr[j]] > m) -- j;
if(suml[sdl[i]]) ans += big(0, sum[j]);
}
// printf("\n[%d]\n", ans);
dfs_ans(cw[tmphvy].u, _n - tmpsiz), dfs_ans(cw[tmphvy].v, tmpsiz);
}
void redfs(int u, int ba) {
for(int i = head[u], v; ~i; i = cw[i].nex) {
v = cw[i].v;
if(v == ba) continue;
redfs(v, u);
vs[u].eb(mk(v, cw[i].w));
}
}
void rebuild() {
redfs(1, 0);
tot = -1;
for(int i = 1; i <= n + n; ++i) head[i] = -1;
for(int i = 1, tx, ty; i <= n; ++i) {
if(SZ(vs[i]) <= 2) {
for(int j = 0; j < SZ(vs[i]); ++j) add_edge(i, vs[i][j].fi, vs[i][j].se);
}else if(SZ(vs[i]) > 2) {
tx = ++ n, ty = ++ n;
add_edge(i, tx, 0), add_edge(i, ty, 0);
for(int j = 0; j < SZ(vs[i]); ++j) {
if(j & 1) vs[ty].eb(vs[i][j]);
else vs[tx].eb(vs[i][j]);
}
}
}
}
void dfs(int u, int ba) {
for(int i = head[u]; ~i; i = cw[i].nex) {
if(cw[i].v == ba) continue;
printf("*%d %d %d %d\n", cw[i].v, u, id[u], cw[i].w);
dfs(cw[i].v, u);
}
}
int main() {
while(~scanf("%d%d", &n, &m) && (n + m)) {
for(int i = 1; i <= n; ++i) head[i] = -1, id[i] = 1;
tot = -1;
for(int i = 1, a, b, c; i < n; ++i) {
scanf("%d%d%d", &a, &b, &c);
add_edge(a, b, c);
}
tn = tot;
rebuild();
// dfs(1, 0);
ans = 0;
dfs_ans(1, n);
printf("%lld\n", ans);
for(int i = 0; i <= tot; ++i) vis[i] = 0;
for(int i = 0; i <= n; ++i) vs[i].clear(), id[i] = 0;
}
return 0;
}
//暴力二叉树建图(一个和原节点对应一个虚节点 300ms
const int MXN = 3e4 + 7;
const int MXE = 6e4 + 7;
int n, m;
struct lp{
int v, nex;
int w, u;
} cw2[MXE];
int tot2, head2[MXN];
int tot, head[MXN], siz[MXN], hvy, hvysiz, vis[MXE];
int stkl[MXN], stkr[MXN], suml[MXN], sumr[MXN], id[MXN], sdl[MXN], sdr[MXN], sum[MXN];
LL ans;
void add_edge2(int a, int b, int c) {
cw2[++ tot2].v = b, cw2[tot2].nex = head2[a], cw2[tot2].w = c, cw2[tot2].u = a;
head2[a] = tot2;
cw2[++ tot2].v = a, cw2[tot2].nex = head2[b], cw2[tot2].w = c, cw2[tot2].u = b;
head2[b] = tot2;
}
void dfs_pre(int u, int ba, int _tn) {
siz[u] = 1;
for(int i = head2[u], v; ~i; i = cw2[i].nex) {
v = cw2[i].v;
if(v == ba || vis[i] || vis[i^1]) continue;
dfs_pre(v, u, _tn);
siz[u] += siz[v];
if(hvy == -1 || big(hvysiz, _tn - hvysiz) > big(siz[v], _tn - siz[v])) {
hvy = i;
hvysiz = siz[v];
}
}
}
void dfs_dep(int f, int u, int ba, int w) {
if(f) stkl[++stkl[0]] = w, suml[stkl[0]] = id[u];
else stkr[++stkr[0]] = w, sumr[stkr[0]] = id[u];
for(int i = head2[u], v; ~i; i = cw2[i].nex) {
v = cw2[i].v;
if(v == ba || vis[i] || vis[i^1]) continue;
dfs_dep(f, v, u, w + cw2[i].w);
}
}
bool cmpl(const int&a, const int&b) {
return stkl[a] < stkl[b];
}
bool cmpr(const int&a, const int&b) {
return stkr[a] < stkr[b];
}
void dfs_ans(int u, int _n) {
if(_n <= 1) return;
hvy = hvysiz = -1;
dfs_pre(u, 0, _n);
// printf("--%d %d %d %d\n", u, hvysiz, cw[hvy].u, cw[hvy].v);
int tmphvy = hvy, tmpsiz = hvysiz;
vis[tmphvy] = vis[tmphvy^1] = 1;
stkl[0] = stkr[0] = 0;
dfs_dep(1, cw2[tmphvy].u, -1, 0), dfs_dep(0, cw2[tmphvy].v, -1, 0);
for(int i = 1; i <= stkl[0]; ++i) sdl[i] = i;
// for(int i = 1; i <= stkl[0]; ++i) printf("%d ", stkl[i]);
// printf("\n");
// for(int i = 1; i <= stkl[0]; ++i) printf("%d ", suml[i]);
// printf("\n");
for(int i = 1; i <= stkr[0]; ++i) sdr[i] = i;
// for(int i = 1; i <= stkr[0]; ++i) printf("%d ", sumr[i]);
// printf("\n");
sort(sdl + 1, sdl + stkl[0] + 1, cmpl), sort(sdr + 1, sdr + stkr[0] + 1, cmpr);
for(int i = 1; i <= stkr[0]; ++i) sum[i] = sum[i-1] + sumr[sdr[i]];
// for(int i = 1; i <= stkr[0]; ++i) printf("%d ", sum[i]);
// printf("\n");
// for(int i = 1; i <= stkr[0]; ++i) printf("%d ", stkr[sdr[i]]);
// printf("\n");
// sort(stkl + 1, stkl + 1 + stkl[0]), sort(stkr + 1, stkr + 1 + stkr[0]);
for(int i = 1, j = stkr[0]; i <= stkl[0]; ++i) {
// printf("%d ", stkl[sdl[i]]);
while(j >= 1 && stkl[sdl[i]] + cw2[tmphvy].w + stkr[sdr[j]] > m) -- j;
if(suml[sdl[i]]) ans += big(0, sum[j]);
}
// printf("\n[%d]\n", ans);
dfs_ans(cw2[tmphvy].u, _n - tmpsiz), dfs_ans(cw2[tmphvy].v, tmpsiz);
}
lp cw[MXE];
void add_edge(int a, int b, int c) {
cw[++ tot].v = b, cw[tot].nex = head[a], cw[tot].w = c, cw[tot].u = a;
head[a] = tot;
cw[++ tot].v = a, cw[tot].nex = head[b], cw[tot].w = c, cw[tot].u = b;
head[b] = tot;
}
vector<pii> vs;
void redfs(int u, int ba) {
vs.clear();
for(int i = head[u], v; ~i; i = cw[i].nex) {
v = cw[i].v;
if(v == ba) continue;
vs.eb(mk(cw[i].v, cw[i].w));
}
if(SZ(vs) <= 2) {
for(int i = 0; i < SZ(vs); ++i) add_edge2(u, vs[i].fi, vs[i].se);
}else {
int lst = -1;
for(int i = 1; i < SZ(vs); ++i) {
if(i == SZ(vs) - 1) {
add_edge2(u, lst, 0);
add_edge2(u, vs[i].fi, vs[i].se);
}else if(i == 1) {
lst = ++ n;
add_edge2(lst, vs[i-1].fi, vs[i-1].se);
add_edge2(lst, vs[i].fi, vs[i].se);
}else {
add_edge2(n + 1, lst, 0);
add_edge2(n + 1, vs[i].fi, vs[i].se);
lst = ++ n;
}
}
}
for(int i = head[u], v; ~i; i = cw[i].nex) {
v = cw[i].v;
if(v == ba) continue;
redfs(v, u);
}
}
void rebuild() {
tot2 = -1;
for(int i = 1; i <= n + n; ++i) head2[i] = -1;
redfs(1, 0);
}
void dfs(int u, int ba) {
// cerr << u << " " << ba << endl;
for(int i = head2[u]; ~i; i = cw2[i].nex) {
if(cw2[i].v == ba) continue;
// printf("*%d %d %d %d\n", cw2[i].v, u, id[u], cw2[i].w);
dfs(cw2[i].v, u);
}
}
int main() {
while(~scanf("%d%d", &n, &m) && (n + m)) {
for(int i = 1; i <= n; ++i) head[i] = -1, id[i] = 1;
tot = -1;
for(int i = 1, a, b, c; i < n; ++i) {
scanf("%d%d%d", &a, &b, &c);
add_edge(a, b, c);
}
rebuild();
ans = 0;
dfs_ans(1, n);
printf("%lld\n", ans);
for(int i = 0; i <= tot2; ++i) vis[i] = 0;
for(int i = 0; i <= n; ++i) id[i] = 0;
}
return 0;
}
动态点分治(点分树)
裸题:洛谷P2056(ZJOI2007)捉迷藏, BZOJ3730震波
题目:CF757G , CF100633D
感觉动态点分治利用点分树的特性使得看似暴力的解法变得合理。毕竟点分树层数在\(log(n)\)级别。
洛谷p2056这道裸题来说:
对每个点维护其在点分树上子树内所有点到它父亲节点的距离信息,和该节点在点分树上每个子节点的所在子树内离他最远的距离(距离信息只需要最远和次远的即可,参考别人用优先队列封装了一个堆)
每次更新就暴力沿着点分树向上更新即可。
复杂度:\(O(nlog(n)^2)\)
const int MXN = 2e5 + 7;
const int MXE = 2e6 + 7;
int n, m;
struct lp {
int v, nex;
int w;
} cw[MXE];
int tot, head[MXN], siz[MXN], hvy, hvysiz, vis[MXE];
int col[MXN], dep[MXN], fa[MXN], dis[MXN][20];
namespace LCA {
int dis[MXN], up[MXN][20];
void dfs(int u, int ba, int d) {
up[u][0] = ba; dis[u] = d;
for(int i = 1; i < 20; ++i) up[u][i] = up[up[u][i-1]][i-1];
for(int i = head[u], v; ~i; i = cw[i].nex) {
v = cw[i].v;
if(v == ba) continue;
dfs(v, u, d + 1);
}
}
int lca(int x, int y) {
if(dis[x] < dis[y]) swap(x, y);
for(int i = 19; i >= 0; --i) {
if(dis[up[x][i]] >= dis[y]) {
x = up[x][i];
}
}
if(x == y) return x;
for(int i = 19; i >= 0; --i) {
if(up[x][i] != up[y][i]) {
x = up[x][i], y = up[y][i];
}
}
return up[x][0];
}
int query(int i, int j) {
return dis[i] + dis[j] - 2 * dis[lca(i, j)];
}
}
struct heap {
priority_queue<int> A, B; // heap=A-B
void insert(int x) { A.push(x); }
void erase(int x) { B.push(x); }
int top() {
while (!B.empty() && A.top() == B.top()) A.pop(), B.pop();
return A.top();
}
void pop() {
while (!B.empty() && A.top() == B.top()) A.pop(), B.pop();
A.pop();
}
int top2() {
int t = top(), ret;
pop();
ret = top();
A.push(t);
return ret;
}
int size() { return A.size() - B.size(); }
} disf[MXN], dison[MXN], ans;
void add_edge(int a, int b) {
cw[++ tot].v = b, cw[tot].nex = head[a];
head[a] = tot;
cw[++ tot].v = a, cw[tot].nex = head[b];
head[b] = tot;
}
int get_dis(int i, int j) {
if(i == j) return 0;
if(dis[i][dep[i] - dep[j]]) return dis[i][dep[i] - dep[j]];
dis[i][dep[i] - dep[j]] = LCA::query(i, j);
return dis[i][dep[i] - dep[j]];
}
void dfs_pre(int u, int ba, int _tn) {
int mm = 0;
siz[u] = 1;
for(int i = head[u], v; ~i; i = cw[i].nex) {
v = cw[i].v;
if(v == ba || vis[v]) continue;
dfs_pre(v, u, _tn);
siz[u] += siz[v];
mm = big(mm, siz[v]);
}
mm = big(mm, _tn - siz[u]);
if(hvy == -1 || hvysiz > mm) {
hvy = u;
hvysiz = mm;
}
}
void dfs_dis(int u, int ba, int d, int rt) {
disf[rt].insert(d);
for(int i = head[u], v; ~i; i = cw[i].nex) {
v = cw[i].v;
if(v == ba || vis[v]) continue;
dfs_dis(v, u, d + 1, rt);
}
}
void dfs_get(int u, int _n, int f) {
if(f) hvy = hvysiz = -1;
else assert(hvy == u);
if(f) dfs_pre(u, -1, _n);
// debug(u, _n, hvy)
int lstrt = hvy;
vis[lstrt] = 1;
for(int i = head[lstrt], v; ~i; i = cw[i].nex) {
v = cw[i].v;
if(v == lstrt || vis[v]) continue;
hvy = hvysiz = -1;
dfs_pre(v, -1, siz[v]);
dfs_dis(v, -1, 1, hvy);
fa[hvy] = lstrt;
dep[hvy] = dep[lstrt] + 1;
// debug(hvy, dep[hvy])
dison[lstrt].insert(disf[hvy].top());
dfs_get(hvy, siz[v], 0);
}
if(SZ(dison[lstrt]) >= 2) {
ans.insert(dison[lstrt].top() + dison[lstrt].top2());
}else if(SZ(dison[lstrt]))ans.insert(dison[lstrt].top());
}
int main() {
#ifndef ONLINE_JUDGE
freopen("/home/cwolf9/CLionProjects/ccc/in.txt", "r", stdin);
//freopen("/home/cwolf9/CLionProjects/ccc/out.txt", "w", stdout);
#endif
n = read();
for(int i = 1; i <= n; ++i) head[i] = -1;
tot = -1;
for(int i = 1, a, b; i < n; ++i) {
a = read(), b = read();
add_edge(a, b);
}
LCA::dfs(1, 0, 1);
dfs_get(1, n, 1);
m = read();
// debug(m, get_dis(1, 3), dep[1], dep[3])
char op[3];
int x, cnt = n;
while(m --) {
scanf("%s", op);
if(op[0] == 'G') {
if(cnt == 0) printf("-1\n");
else if(cnt == 1) printf("0\n");
else printf("%d\n", ans.top());
}else {
x = read();
if(col[x] == 0) {
if(SZ(dison[x]) == 1) ans.erase(dison[x].top());
for(int i = x; fa[i]; i = fa[i]) {
if(SZ(dison[fa[i]]) >= 2) ans.erase(dison[fa[i]].top() + dison[fa[i]].top2());
else if(col[fa[i]] == 0 && SZ(dison[fa[i]]) == 1) ans.erase(dison[fa[i]].top());
dison[fa[i]].erase(disf[i].top());
disf[i].erase(get_dis(x, fa[i]));
if(disf[i].size()) dison[fa[i]].insert(disf[i].top());
if(SZ(dison[fa[i]]) >= 2) ans.insert(dison[fa[i]].top() + dison[fa[i]].top2());
else if(col[fa[i]] == 0 &&SZ(dison[fa[i]])) ans.insert(dison[fa[i]].top());
}
col[x] = 1;
-- cnt;
}else {
if(SZ(dison[x]) == 1) ans.insert(dison[x].top());
for(int i = x; fa[i]; i = fa[i]) {
if(SZ(dison[fa[i]]) >= 2) ans.erase(dison[fa[i]].top() + dison[fa[i]].top2());
else if(col[fa[i]] == 0 &&SZ(dison[fa[i]]) == 1) ans.erase(dison[fa[i]].top());
if(SZ(disf[i])) dison[fa[i]].erase(disf[i].top());
disf[i].insert(get_dis(x, fa[i]));
dison[fa[i]].insert(disf[i].top());
if(SZ(dison[fa[i]]) >= 2) ans.insert(dison[fa[i]].top() + dison[fa[i]].top2());
else if(col[fa[i]] == 0 &&SZ(dison[fa[i]])) ans.insert(dison[fa[i]].top());
}
col[x] = 0;
++ cnt;
}
}
}
#ifndef ONLINE_JUDGE
cout << "time cost:" << 1.0*clock()/CLOCKS_PER_SEC << "ms" << endl;
#endif
return 0;
}
/*
* http://wiki.gyh.me/DP/%E6%A0%91%E5%88%86%E6%B2%BB/
*/
bzoj3730震波
疯狂tle,好像网上动态点分治的代码全都过不了。。
const int MXN = 2e5 + 7;
const int MXE = 2e5 + 7;
int n, m;
const int N = 100002;
struct lp {
int v, nex;
int w;
} cw[MXE];
int tot, head[MXN], siz[MXN], hvy, hvysiz;
bool vis[MXN];
int ar[MXN], dep[MXN], fa[MXN];
int dis[MXN][20];
namespace LCA {
int dis[MXN], up[MXN][20], lg[MXN];
void dfs(int u, int ba) {
for(int i = head[u], v; ~i; i = cw[i].nex) {
v = cw[i].v;
if(v == ba) continue;
dis[v] = dis[u] + 1, up[v][0] = u;
dfs(v, u);
}
}
void init() {
for (int i = 2; i <= n; ++i) lg[i] = lg[i / 2] + 1;;
dis[1] = 1;
dfs(1, -1);
for (int j = 1; j <= lg[n]; ++j)
for (int i = 1; i <= n; ++i) up[i][j] = up[up[i][j - 1]][j - 1];
}
int lca(int x, int y) {
if (dis[x] > dis[y]) swap(x, y);
int k = dis[y] - dis[x];
for (int i = 0; k; k = k / 2, ++i)
if (k & 1) y = up[y][i];
if (x == y) return x;
k = dis[x];
for (int i = lg[k]; i >= 0; --i)
if (up[x][i] != up[y][i]) x = up[x][i], y = up[y][i];
return up[x][0];
}
int query(int i, int j) {
return dis[i] + dis[j] - 2 * dis[lca(i, j)];
}
}
int get_dis(int i, int j) {
if(i == j) return 0;
if(dis[i][dep[i] - dep[j]]) return dis[i][dep[i] - dep[j]];
dis[i][dep[i] - dep[j]] = LCA::query(i, j);
return dis[i][dep[i] - dep[j]];
}
struct heap {
static const int MAXE = N*60;
int rt[MXN], tot;
int sum[MAXE], ls[MAXE], rs[MAXE];
void update(int p, int v, int l, int r, int&rt) {
if(rt == 0) rt = ++ tot;
if(l == r) {
sum[rt] += v;
return;
}
int mid = (l + r) >> 1;
if(p <= mid) update(p, v, l, mid, ls[rt]);
else update(p, v, mid + 1, r, rs[rt]);
sum[rt] = sum[ls[rt]] + sum[rs[rt]];
}
int query(int L, int R, int l, int r, int rt) {
if(L > R || rt == 0 || L > r || R < l) return 0;
if(L <= l && r <= R) return sum[rt];
int mid = (l + r) >> 1;
if(L > mid) return query(L, R, mid + 1, r, rs[rt]);
else if(R <= mid) return query(L,R,l,mid,ls[rt]);
else {
return query(L,mid,l,mid,ls[rt])+query(mid+1,R,mid+1,r,rs[rt]);
}
}
} dison, disfa;
inline void add_edge(int a, int b) {
cw[++ tot].v = b, cw[tot].nex = head[a];
head[a] = tot;
cw[++ tot].v = a, cw[tot].nex = head[b];
head[b] = tot;
}
int _tn;
void dfs_pre(int u, int ba) {
int mm = 0;
siz[u] = 1;
for(int i = head[u], v; ~i; i = cw[i].nex) {
v = cw[i].v;
if(v == ba || vis[v]) continue;
dfs_pre(v, u);
siz[u] += siz[v];
mm = big(mm, siz[v]);
}
mm = big(mm, _tn - siz[u]);
if(hvy == -1 || hvysiz > mm) {
hvy = u;
hvysiz = mm;
}
}
void dfs_dis(int u, int ba, int d, int rt) {
dison.update(d, ar[u], 0, n, dison.rt[rt]);
siz[u] = 1;
for(int i = head[u], v; ~i; i = cw[i].nex) {
v = cw[i].v;
if(v == ba || vis[v]) continue;
dfs_dis(v, u, d + 1, rt);
siz[u] += siz[v];
}
}
void dfs_fa(int u, int ba, int d, int rt) {
disfa.update(d, ar[u], 0, n, disfa.rt[rt]);
for(int i = head[u], v; ~i; i = cw[i].nex) {
v = cw[i].v;
if(v == ba || vis[v]) continue;
dfs_fa(v, u, d + 1, rt);
}
}
void dfs_get(int u) {
// debug(u, _n, hvy)
vis[u] = true;
dfs_dis(u, -1, 0, u);
for(int i = head[u], v; ~i; i = cw[i].nex) {
v = cw[i].v;
if(vis[v]) continue;
hvy = hvysiz = -1;
// int tmp = siz[v];
_tn = siz[v];
dfs_pre(v, -1);
dfs_fa(v, u, 1, hvy);
// debug(u, v, tmp, siz[v])
// assert(tmp == siz[v]);
fa[hvy] = u;
dep[hvy] = dep[u] + 1;
dfs_get(hvy);
}
}
int main() {
n = read(), m = read();
for(int i = 1; i <= n; ++i) ar[i] = read(), head[i] = -1;
tot = -1;
for(int i = 1, a, b; i < n; ++i) {
a = read(), b = read();
add_edge(a, b);
}
LCA::init();
hvy = hvysiz = -1;
_tn = n;
dfs_pre(1, -1);
dep[hvy] = 1;
dfs_get(hvy);
// debug(m, get_dis(1, 3), dep[1], dep[3])
int opt, x, y, lst, lastans = 0;
while(m --) {
opt = read(), x = read(), y = read();
x ^= lastans, y ^= lastans;
if(opt == 0) {
lastans = dison.query(0, y, 0, n, dison.rt[x]);
for(int i = x; fa[i]; i = fa[i]) {
// debug(i, fa[i])
lst = get_dis(x, fa[i]);
lastans += dison.query(0, y - lst, 0, n, dison.rt[fa[i]]) -
disfa.query(0, y - lst, 0, n, disfa.rt[i]);
}
printf("%d\n", lastans);
}else {
dison.update(0, y - ar[x], 0, n, dison.rt[x]);
for(int i = x; fa[i]; i = fa[i]) {
lst = get_dis(x, fa[i]);
dison.update(lst, y - ar[x], 0, n, dison.rt[fa[i]]);
disfa.update(lst, y - ar[x], 0, n, disfa.rt[i]);
}
ar[x] = y;
}
}
return 0;
}
其他
KD-Tree
bzoj2648,bzoj2716
概念参考:guoziqing506, 拳四郎, July_
两种码风:devil-gary, RZZ
const int INF = 0x3f3f3f3f;
const int MXN = 5e5 + 7;
const int MXE = 1e6 + 7;
int n, m, Q;
int ans;
//动态开点
const int K = 2;
struct P {
int x[K];
}ap[MXN], pt;
struct KDT {
int x[K], Min[K], Max[K], split;
int ls, rs;
}kdt[MXE];
int kdtNode, kdtRt;
int cmp_k;
bool cmp(const P&a, const P&b) {
return a.x[cmp_k] < b.x[cmp_k];
}
void push_up(int rt) {
int lc = kdt[rt].ls;
if(lc) {
for(int i = 0; i < K; ++i) {
kdt[rt].Min[i] = sml(kdt[rt].Min[i], kdt[lc].Min[i]);
kdt[rt].Max[i] = big(kdt[rt].Max[i], kdt[lc].Max[i]);
}
}
lc = kdt[rt].rs;
if(lc) {
for(int i = 0; i < K; ++i) {
kdt[rt].Min[i] = sml(kdt[rt].Min[i], kdt[lc].Min[i]);
kdt[rt].Max[i] = big(kdt[rt].Max[i], kdt[lc].Max[i]);
}
}
}
void build(int l, int r, int &rt) {
if(l > r) {
rt = 0;
return;
}
rt = ++ kdtNode;
int d = 0;
double mx = -1, ave, sum;
for(int i = 0; i < K; ++i) {
ave = sum = 0;
for(int j = l; j <= r; ++j) ave += ap[j].x[i]; ave /= r-l+1;
for(int j = l; j <= r; ++j) sum += o2(ap[j].x[i] - ave);
if(sum > mx) mx = sum, d = i;
}
kdt[rt].split = cmp_k = d;
int mid = (l + r) >> 1;
nth_element(ap + l, ap + mid + 1, ap + r + 1, cmp);
for(int i = 0; i < K; ++i) kdt[rt].x[i] = kdt[rt].Min[i] = kdt[rt].Max[i] = ap[mid].x[i];
build(l, mid - 1, kdt[rt].ls), build(mid + 1, r, kdt[rt].rs);
push_up(rt);
}
void insert(int &rt, int kd = 0) {
if(!rt) {
rt = ++ kdtNode;
kdt[rt].split = (kd + 1) % K;
for(int i = 0; i < K; ++i) kdt[rt].x[i] = kdt[rt].Min[i] = kdt[rt].Max[i] = pt.x[i];
return;
}
for(int i = 0; i < K; ++i) {
kdt[rt].Min[i] = sml(kdt[rt].Min[i], pt.x[i]);
kdt[rt].Max[i] = big(kdt[rt].Max[i], pt.x[i]);
}
int d = kdt[rt].split;
if(pt.x[d] < kdt[rt].x[d]) insert(kdt[rt].ls, d);
else insert(kdt[rt].rs, d);
push_up(rt);//?
}
inline int dist(int *x, int *y) {
int ret = 0;
for(int i = 0; i < K; ++i) ret += abs(x[i] - y[i]);
return ret;
}
inline int dist(int *x, int *y, int *z) {//判断是否有交
int ret = 0;
for(int i = 0; i < K; ++i) {
if(y[i] > x[i]) ret += y[i] - x[i];
else if(x[i] > z[i]) ret += x[i] - z[i];
}
return ret;
}
void knnask(int rt) {
if(!rt || dist(pt.x, kdt[rt].Min, kdt[rt].Max) >= ans) return;
ans = sml(ans, dist(kdt[rt].x, pt.x));
int d = kdt[rt].split;
if(pt.x[d] < kdt[rt].x[d]) knnask(kdt[rt].ls), knnask(kdt[rt].rs);
else knnask(kdt[rt].rs), knnask(kdt[rt].ls);
}
int main() {
#ifndef ONLINE_JUDGE
freopen("/home/cwolf9/CLionProjects/ccc/in.txt", "r", stdin);
// freopen("/home/cwolf9/CLionProjects/ccc/out.txt", "w", stdout);
#endif
n = read(), m = read();
for(int i = 1; i <= n; ++i) {
for(int j = 0; j < K; ++j) ap[i].x[j] = read();
}
kdtNode = kdtRt = 0;
build(1, n, kdtRt);
int opt, x, y;
while(m --) {
opt = read();
for(int i = 0; i < K; ++i) pt.x[i] = read();
if(opt == 1) insert(kdtRt);
else {
ans = INF;
knnask(kdtRt);
printf("%d\n", ans);
}
}
return 0;
}
虚树
虚树就是把关键点从树立面拿出来,再补上他们的lca,形成新的树。
讲解:here
bzoj2286,bzoj3611,cf613D,bzoj3572
namespace VT {
struct lp {
int v, nex;
LL w;
} cw[MXE];
int tot, head[MXN], stak[MXN], top, d[MXN], is[MXN];
void add_edge(int a, int b, LL c) {
cw[++tot].v = b, cw[tot].nex = head[a], cw[tot].w = c;
head[a] = tot;
cw[++tot].v = a, cw[tot].nex = head[b], cw[tot].w = c;
head[b] = tot;
}
bool cmp(const int &a, const int &b) {///tid is dfs number
return LCA::tid[ar[a]] < LCA::tid[ar[b]];
}
void build(int n) {
tot = head[1] = -1;
stak[top = 1] = 1;
for(int i = 1; i <= n; ++i) d[i] = i, is[ar[i]] = 1;
sort(d + 1, d + 1 + n, cmp);
for(int i = 1; i <= n; ++i) {
if(ar[d[i]] == 1) continue;
int lca = LCA::lca(ar[d[i]], stak[top]);
if(lca != stak[top]) {
while(LCA::tid[lca] < LCA::tid[stak[top-1]]) {
add_edge(stak[top], stak[top-1], LCA::query(stak[top], stak[top-1]));
-- top;
}
if(lca == stak[top-1]) {
add_edge(stak[top], stak[top-1], LCA::query(stak[top], stak[top-1]));
-- top;
}else {
head[lca] = -1;
add_edge(lca, stak[top], LCA::query(lca, stak[top]));
stak[top] = lca;
}
}
stak[++top] = ar[d[i]];
head[stak[top]] = -1;
}
for(int i = 2; i <= top; ++i) add_edge(stak[i], stak[i-1], LCA::query(stak[i], stak[i-1]));
}
LL dp[MXN];
void dfs(int u, int ba) {
dp[u] = 0;
for(int i = head[u], v; ~i; i = cw[i].nex) {
v = cw[i].v;
if(v == ba) continue;
dfs(v, u);
if(is[v]) dp[u] += cw[i].w;
else dp[u] += sml(dp[v], cw[i].w);
}
}
}
steiner斯坦纳树
2019南昌邀请赛A题,HDU4085
这两题可以说几乎一样了,hdu4085只要求前k(\(k\le 5)\)个点和后k个点两两之间一一对应,但并没有固定谁该和谁对应。
邀请赛A题给定4对点,固定两两之间应该联通。写法差不多,改一下\(check()\)状态的函数即可,合法状态要么不包含给定点对,要么给定点对的两个点都包含。
注意有一个坑点是4对点中可能有重点。
/*
枚举状态sta时,遇到在第一类转移之后有更新的节点,将其加入队列,用spfa做一次迭代更新。普通的转
移是不能保证最优性的,而spfa采用迭代逼近的方式,将更新过的点再进行更新,可以保证最后每个节点
都是“最短路”。
对于ZOJ WormHole Transportation 和 HDU Peach Blossom Spring,其解为一片斯坦纳生成
森林,用dp[sta]数组来维护最优解,可利用f[i][sta]来松弛dp[sta],得到当前状态的最小代价。
最后由小到大枚举状态,将森林在0代价的条件下合并成“生成树”即可。
注意在这过程中要过滤掉非法状态。
*/
const int MXN = 1e3 + 7;
const int MXE = 1e4 + 7;
int n, m, stnum;
int head[MXN], tot;
struct lp {
int v, nex;
int w;
}cw[MXE];
LL dp[(1<<10) + 5][35], val[MXN], f[(1<<10)+5];
int in[MXN];
queue<int> Q;
string a, b, aa[4], bb[4];
int X[4], Y[4], to[MXN], re[MXN];
map<string, int> mp, mp2;
void add_edge(int u, int v, int w) {
cw[++tot].v = v, cw[tot].nex = head[u], cw[tot].w = w;
head[u] = tot;
cw[++tot].v = u, cw[tot].nex = head[v], cw[tot].w = w;
head[v] = tot;
}
void spfa(int S) {
while(!Q.empty()) {
int u = Q.front(); Q.pop();
in[u] = 0;
for(int i = head[u], v; ~i; i = cw[i].nex) {
v = cw[i].v;
if(dp[S][v] > dp[S][u] + cw[i].w) {
dp[S][v] = dp[S][u] + cw[i].w;
if(!in[v]) Q.push(v), in[v] = 1;
}
}
}
}
void steiner() {
clr(dp, 0x3f);
for(int i = 0; i < stnum; ++i) {
dp[1<<i][re[i]] = 0;
}
int sta = 1 << stnum;
for (int S = 1; S < sta; ++S) {
for (int x = S; x; x = (x-1)&S) {
for(int i = 0; i < n; ++i) {
dp[S][i] = sml(dp[S][i], dp[x][i] + dp[S^x][i]);
}
}
for(int i = 0; i < n; ++i) {
in[i] = 0;
if(dp[S][i] != INFLL && !in[i]) {
Q.push(i), in[i] = 1;
}
}
spfa(S);
}
}
bool check(int s) {
bool flag = true;
for(int i = 0; i < 4; ++i) {
if(((s>>to[X[i]])&1) != ((s>>to[Y[i]])&1)) flag = false;
}
return flag;
}
int main() {
#ifndef ONLINE_JUDGE
freopen("/home/cwolf9/CLionProjects/ccc/in.txt", "r", stdin);
//freopen("/home/cwolf9/CLionProjects/ccc/out.txt", "w", stdout);
#endif
tot = -1;
cin >> n >> m;
for(int i = 0; i < n; ++i) {
cin >> a;
mp[a] = i;
head[i] = -1;
}
for(int i = 1, x; i <= m; ++i) {
cin >> a >> b >> x;
add_edge(mp[a], mp[b], x);
}
for(int i = 0; i < 4; ++i) {
cin >> aa[i] >> bb[i];
X[i] = mp[aa[i]], Y[i] = mp[bb[i]];
if(mp2[aa[i]] == 0) to[X[i]] = stnum ++, re[stnum - 1] = X[i];
if(mp2[bb[i]] == 0) to[Y[i]] = stnum ++, re[stnum - 1] = Y[i];
mp2[aa[i]] = mp2[bb[i]] = 1;
}
steiner();
int sta = 1 << stnum;
clr(f, 0x3f);
for(int i = 1; i < sta; ++i) {
for(int j = 0; j < n; ++j) f[i] = sml(f[i], dp[i][j]);
}
// for(int i = 1; i < sta; ++ i) printf("%d ", f[i]); printf("\n");
for(int i = 1; i < sta; ++i) {
if(check(i)) {
for (int x = i; x; x = (x-1)&i) {
if(check(x)) f[i] = sml(f[i], f[x] + f[i ^ x]);
}
}
}
cout << f[sta - 1] << endl;
#ifndef ONLINE_JUDGE
cout << "time cost:" << 1.0*clock()/CLOCKS_PER_SEC << "ms" << endl;
#endif
return 0;
}
const int MXN = 1e3 + 7;
const int MXE = 1e4 + 7;
int n, m;
int head[MXN], tot;
struct lp {
int v, nex;
int w;
}cw[MXE];
int dp[(1<<10) + 5][35], val[MXN], f[(1<<10)+5];
int in[MXN];
queue<int> Q;
int P[MXN], T[MXN], X[MXN], Y[MXN], pnum[MXN], re[MXN], t1, t2, stnum;
void add_edge(int u, int v, int w) {
cw[++tot].v = v, cw[tot].nex = head[u], cw[tot].w = w;
head[u] = tot;
cw[++tot].v = u, cw[tot].nex = head[v], cw[tot].w = w;
head[v] = tot;
}
void spfa(int S) {
while(!Q.empty()) {
int u = Q.front(); Q.pop();
in[u] = 0;
for(int i = head[u], v; ~i; i = cw[i].nex) {
v = cw[i].v;
if(dp[S][v] > dp[S][u] + cw[i].w) {
dp[S][v] = dp[S][u] + cw[i].w;
if(!in[v]) Q.push(v), in[v] = 1;
}
}
}
}
void steiner() {
clr(dp, 0x3f);
for(int i = 0; i < stnum; ++i) {
dp[1<<i][re[i]] = 0;
}
int sta = 1 << stnum;
for (int S = 1; S < sta; ++S) {
for (int x = S; x; x = (x-1)&S) {
for(int i = 0; i < n; ++i) {
dp[S][i] = sml(dp[S][i], dp[x][i] + dp[S^x][i]);
}
}
for(int i = 0; i < n; ++i) {
in[i] = 0;
if(dp[S][i] != INFLL && !in[i]) {
Q.push(i), in[i] = 1;
}
}
spfa(S);
}
}
bool check(int s) {
int cnt = 0;
for(int i = 0; i < stnum; ++i) if(s&(1<<i)) cnt += pnum[i];
return cnt >= 0;
}
int get(int s) {
int cnt = 0, cnt2 = 0;
for(int i = 0; i < stnum; ++i) {
if(s&(1<<i)) {
if (pnum[i] >= 0) cnt += pnum[i];
else cnt2 -= pnum[i];
}
}
if(cnt <= cnt2) return cnt;
return 0;
}
int main() {
#ifndef ONLINE_JUDGE
freopen("/home/cwolf9/CLionProjects/ccc/in.txt", "r", stdin);
//freopen("/home/cwolf9/CLionProjects/ccc/out.txt", "w", stdout);
#endif
tot = -1;
n = read();
for(int i = 0; i < n; ++i) {
P[i] = read(), T[i] = read();
if(P[i] && T[i]) -- P[i], -- T[i];
if(P[i] || T[i]) {
pnum[stnum] = P[i] - T[i];
re[stnum] = i;
++ stnum;
}
head[i] = -1;
}
m = read();
for(int i = 1, u, v, w; i <= m; ++i) {
u = read(), v = read(), w = read();
add_edge(u - 1, v - 1, w);
}
steiner();
int sta = 1 << stnum;
clr(f, 0x3f);
for(int i = 1; i < sta; ++i) {
for(int j = 0; j < n; ++j) f[i] = sml(f[i], dp[i][j]);
}
// for(int i = 1; i < sta; ++ i) printf("%d ", f[i]); printf("\n");
for(int i = 1; i < sta; ++i) {
if(check(i)) {
for (int x = i; x; x = (x-1)&i) {
if(check(x) && check(i ^ x)) {
// debug(i, x, i ^ x, f[i], f[x], f[i^x])
f[i] = sml(f[i], f[x] + f[i ^ x]);
}
}
}
}
int Maxnum = 0, Mincost = 0;
for(int i = 1; i < sta; ++i) {
if(f[i] != INF) {
int num = get(i);
if(num > Maxnum) {
Maxnum = num;
Mincost = f[i];
}else if(num == Maxnum && f[i] < Mincost) {
Mincost = f[i];
}
}
}
printf("%d %d\n", Maxnum, Mincost);
#ifndef ONLINE_JUDGE
cout << "time cost:" << 1.0*clock()/CLOCKS_PER_SEC << "ms" << endl;
#endif
return 0;
}