杂题 Part II
QOJ10272
首先发现我们不用添加区间两端都不是绝对众数的边,证明比较显然。
再考虑两边都是绝对众数的边,有很多种方法,比如令 \(a_i = x\) 处的 \(b_i = 1\),否则为 \(-1\),那么考虑求出 \(b_i\) 的前缀和 \(c_i\),发现 \((u, v)\) 存在当且仅当 \(c_u \le c_v\),于是可以单调栈,或者进一步发掘性质。
再考虑两边只有一边是绝对众数的边,不妨设为左端点,发现对于一个右端点,最优左端点是固定的,于是我们对上述的每个 \(a_i\) 分开考虑,每次只考虑两个 \(a_i\) 之间的区间,然后又发现连边是一个区间,于是可以差分处理。
CF1305G
一开始就读错题了。
考虑两个点建边 \(a_i + a_j\),加一个 \(a_i = 0\) 的点,最终 MST 边权减去点权即为答案。
考虑 Borvuka,用 \(\land\) 表示与,我们怎么找到 \(a_i \land a_j = 0\) 的点?
我们先找到 \(\max_{a_j \land S = a_j}a_j\),这个是容易的,然后有一个很巧妙的地方是我们只需找到与最大值不在一个连通块内的最大值,则必有一个符合要求,所以做完了。
CF1039D
考虑我们能够贪心 \(O(n)\) 求出 \(k = k_0\) 时的答案,然后考虑答案取值只有 \(O(\sqrt n)\) 种,做完了。
CF1842F
一个想法是拆绝对值之后树形 dp,但是不好搞。
考虑钦定虚树重心之后绝对值就没了,答案也容易贪心求出。
UOJ388
整理一下,对于给定的一个区间,边 \((u, v)\) 有贡献,当且仅当割掉这条边后,两边连通块都有奇数个被选择的点。
于是我们不妨考虑一个子树,若序列中的 \(x\) 在子树内,令它的权值为 \(1\),否则为 \(0\),求出奇数或偶数位置上有多少个位置前缀和是奇数或偶数,就容易计算贡献。
发现用线段树可以实现单点修改,使用 dsu on tree 就可以做到 \(O(n \log^2 n)\)。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
inline ll Read() {
int sig = 1; ll num = 0; char c = getchar();
while(!isdigit(c)) { if(c == '-') sig = -1; c = getchar(); }
while(isdigit(c)) num = (num << 3) + (num << 1) + (c ^ 48), c = getchar();
return num * sig;
}
void Write(ll x) {
if(x < 0) putchar('-'), x = -x;
if(x > 9) Write(x / 10);
putchar((x % 10) ^ 48);
}
const int N = 100005;
const ll Mod = 998244353;
int n, m, siz[N], hson[N]; ll ans;
vector<pair<int, ll> > e[N];
vector<int> vec[N];
struct SegTree {
struct tNode { int l, r, sum, cnt[2][2]; }a[N << 2];
void PushUp(int u) {
bool p = (a[u << 1].r - a[u << 1].l + 1) & 1, q = a[u << 1].sum; a[u].sum = a[u << 1].sum ^ a[u << 1 | 1].sum;
a[u].cnt[0][0] = a[u << 1].cnt[0][0] + a[u << 1 | 1].cnt[p][q], a[u].cnt[0][1] = a[u << 1].cnt[0][1] + a[u << 1 | 1].cnt[p][q ^ 1];
a[u].cnt[1][0] = a[u << 1].cnt[1][0] + a[u << 1 | 1].cnt[p ^ 1][q], a[u].cnt[1][1] = a[u << 1].cnt[1][1] + a[u << 1 | 1].cnt[p ^ 1][q ^ 1];
}
void Build(int u, int l, int r) {
a[u].l = l, a[u].r = r; if(l == r) { a[u].sum = a[u].cnt[0][0] = a[u].cnt[0][1] = a[u].cnt[1][1] = 0, a[u].cnt[1][0] = 1; return ; }
int mid = (l + r) >> 1; Build(u << 1, l, mid), Build(u << 1 | 1, mid + 1, r), PushUp(u);
}
void Modify(int u, int x, int y) {
int l = a[u].l, r = a[u].r, mid = (l + r) >> 1;
if(l == r) { a[u].sum = y, a[u].cnt[0][0] = a[u].cnt[0][1] = a[u].cnt[1][y ^ 1] = 0, a[u].cnt[1][y] = 1; return ; }
Modify(u << 1 | (x > mid), x, y), PushUp(u);
}
}seg;
void Dfs(int u, int fa) {
siz[u] = 1;
for(auto p : e[u]) {
int v = p.first;
if(v != fa) {
Dfs(v, u), siz[u] += siz[v];
if(siz[hson[u]] < siz[v]) hson[u] = v;
}
}
}
void Ins(int u, int x) { for(auto v : vec[u]) seg.Modify(1, v, x); }
void Add(int u, int fa, int x) { Ins(u, x); for(auto p : e[u]) { int v = p.first; if(v != fa) Add(v, u, x); } }
void Dfs2(int u, int fa, ll w, bool o) {
for(auto p : e[u]) { int v = p.first; if(v != fa && v != hson[u]) Dfs2(v, u, p.second, false); }
if(hson[u]) for(auto p : e[u]) if(p.first == hson[u]) Dfs2(hson[u], u, p.second, true);
for(auto p : e[u]) { int v = p.first; if(v != fa && v != hson[u]) Add(v, u, 1); }
Ins(u, 1), ans = (ans + (1ll * seg.a[1].cnt[0][0] * seg.a[1].cnt[0][1] + 1ll * seg.a[1].cnt[1][0] * seg.a[1].cnt[1][1]) % Mod * w) % Mod;
if(!o) Add(u, fa, 0);
}
int main() {
int i; n = Read(), m = Read(), seg.Build(1, 0, m);
for(i = 1; i < n; i++) { int u = Read(), v = Read(); ll w = Read(); e[u].emplace_back(v, w), e[v].emplace_back(u, w); }
for(i = 1; i <= m; i++) vec[Read()].emplace_back(i);
Dfs(1, 0), Dfs2(1, 0, 0ll, false), Write(ans);
}
P14428
核心结论:在任意三点不共线的条件下,对于每对符合要求的三角形,有且仅有两条直线,使得该直线把两个三角形分到两个半平面内,且该直线与两个三角形都相交。
感性理解比较简单,直接任取能分开两个三角形的一条直线,然后分别逆时针或顺时针旋转。
于是我们枚举两个点作为切点,把两个半平面内的点的颜色个数算出来就可以了,这一步极角排序一下然后容易双指针找到一个半平面。注意对于两个点分别属于哪个半平面要讨论一下。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
inline ll Read() {
int sig = 1; ll num = 0; char c = getchar();
while(!isdigit(c)) { if(c == '-') sig = -1; c = getchar(); }
while(isdigit(c)) num = (num << 3) + (num << 1) + (c ^ 48), c = getchar();
return num * sig;
}
void Write(ll x) {
if(x < 0) putchar('-'), x = -x;
if(x > 9) Write(x / 10);
putchar((x % 10) ^ 48);
}
const int N = 3005;
int n;
struct tNode {
int x, y, c; double d;
friend bool operator <(tNode a, tNode b) { return a.d < b.d; }
}a[N], b[N << 1];
bool Check(tNode x, tNode y) { return 1ll * x.x * y.y - 1ll * x.y * y.x >= 0; }
int main() {
int i, j, k = 0, tot[3] = {0, 0, 0}; ll ans = 0; n = Read();
for(i = 1; i <= n; i++) a[i].x = Read(), a[i].y = Read(), a[i].c = Read(), tot[a[i].c]++;
for(i = 1; i <= n; i++) {
int t = 0, cnt[3] = {0, 0, 0}; k = 0, cnt[a[i].c] = 1;
for(j = 1; j <= n; j++) if(j != i) b[++k] = {a[j].x - a[i].x, a[j].y - a[i].y, a[j].c, atan2(a[j].y - a[i].y, a[j].x - a[i].x)};
sort(b + 1, b + k + 1); for(j = 1; j <= k; j++) b[j + k] = b[j];
for(j = 1; j <= k; j++) {
while(t < k + j - 1 && Check(b[j], b[t + 1])) cnt[b[++t].c]++;
ll res = 1ll; cnt[b[j].c]--;
if(a[i].c == 0) res = cnt[1] * cnt[2];
else if(a[i].c == 1) res = cnt[0] * cnt[2];
else res = cnt[0] * cnt[1];
if(b[j].c == 0) res *= (tot[1] - cnt[1]) * (tot[2] - cnt[2]);
else if(b[j].c == 1) res *= (tot[0] - cnt[0]) * (tot[2] - cnt[2]);
else res *= (tot[0] - cnt[0]) * (tot[1] - cnt[1]);
ans += res, res = 1ll, cnt[a[i].c]--, cnt[b[j].c]++;
if(b[j].c == 0) res = cnt[1] * cnt[2];
else if(b[j].c == 1) res = cnt[0] * cnt[2];
else res = cnt[0] * cnt[1];
if(a[i].c == 0) res *= (tot[1] - cnt[1]) * (tot[2] - cnt[2]);
else if(a[i].c == 1) res *= (tot[0] - cnt[0]) * (tot[2] - cnt[2]);
else res *= (tot[0] - cnt[0]) * (tot[1] - cnt[1]);
ans += res, cnt[a[i].c]++, cnt[b[j].c]--;
}
}
Write(ans / 4);
}
P14470
考虑一步之后显然只有 \(O(nV)\) 个区间,然后长度从小到大(或从左到右)计算就可以了,感觉 BIT 常数很小,也可以启发式合并做到严格 \(O(nV^2)\) 级别的复杂度。
P10681
考虑枚举总 \(1\) 的个数 \(s\),然后考虑先确定 \(2\) 放在哪一行或列,再确定每一列的 \(2\) 堆到哪一行中,这里行是区分的,有 \(\frac{s!}{2^{s - m}}\) 的系数(先任意排列,但是一列的球不区分),但是可能会出现两个一列的球排到同一行的情况,直接对这个容斥就可以了,注意同一个行内可能出现 \(AB\) 及 \(BA\) 的本质相同情况,最后要乘以 \(2^{n - s}\)。
AGC020F
考虑先断环为链(把最长的圆弧放到第一位,避免各种特殊情况),然后把实数坐标拆开,发现每种小数部分的相对顺序等概率出现,于是可以钦定后直接状压暴力 dp。
CF618F
考虑猜测一定有解,求 \(a, b\) 的前缀和 \(a', b'\),找到最后一个不比 \(b_i'\) 大的 \(a_j'\),若 \(b_i' = a_j'\) 直接输出,否则由于值域是 \([1, n]\),容易发现 \(b_i' - a_j' < n\),于是根据抽屉原理可以直接凑出一个解。

浙公网安备 33010602011771号