ZJOI 2019 简要题解
从这里开始
感觉就没几个题能写,不过暴力分确实给的很多。每日一吹 scoi 2019
Round 1
Problem A 麻将
考虑怎么判断,先判断有没有超过 $7$ 种大小大于等于 2 ,然后依次考虑每种大小,设 $f_{i, j, 0/1}$ 表示前一种和前面第 2 种分别留下了多少个用于凑面子,以及有没有对子的情况下最多能凑出多少面子。注意到 $j \geqslant i$,所以只用记录 $j' = j - i$,又因连续三个留下了至少 3 个,可以变成 $(x, x, x), (x + 1, x + 1, x + 1), (x + 2, x + 2, x+2)$,因此有 $j', i \leqslant 2$。
然后做一些简单标准化然后爆搜发现状态数并不多。注意 dp 值不能超过 4 。
剩下就考虑选出 $i$ 张牌还没有胡的概率。相信这个大家都会。
Code
#include <bits/stdc++.h>
using namespace std;
typedef bool boolean;
template <typename T>
bool vmax(T& a, T b) {
return a < b ? (a = b, true) : false;
}
#define ll long long
void exgcd(int a, int b, int& x, int& y) {
if (!b) {
x = 1, y = 0;
} else {
exgcd(b, a % b, y, x);
y -= (a / b) * x;
}
}
int inv(int a, int n) {
int x, y;
exgcd(a, n, x, y);
return (x < 0) ? (x + n) : (x);
}
const int Mod = 998244353;
template <const int Mod = :: Mod>
class Z {
public:
int v;
Z() : v(0) { }
Z(int x) : v(x){ }
Z(ll x) : v(x % Mod) { }
friend Z operator + (const Z& a, const Z& b) {
int x;
return Z(((x = a.v + b.v) >= Mod) ? (x - Mod) : (x));
}
friend Z operator - (const Z& a, const Z& b) {
int x;
return Z(((x = a.v - b.v) < 0) ? (x + Mod) : (x));
}
friend Z operator * (const Z& a, const Z& b) {
return Z(a.v * 1ll * b.v);
}
friend Z operator ~(const Z& a) {
return inv(a.v, Mod);
}
friend Z operator - (const Z& a) {
return Z(0) - a;
}
Z& operator += (Z b) {
return *this = *this + b;
}
Z& operator -= (Z b) {
return *this = *this - b;
}
Z& operator *= (Z b) {
return *this = *this * b;
}
friend boolean operator == (const Z& a, const Z& b) {
return a.v == b.v;
}
};
Z<> qpow(Z<> a, int p) {
Z<> rt = Z<>(1), pa = a;
for ( ; p; p >>= 1, pa = pa * pa) {
if (p & 1) {
rt = rt * pa;
}
}
return rt;
}
typedef Z<> Zi;
typedef class Status {
public:
int f[2][3][3], cnt;
void reset() {
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 3; j++) {
for (int k = 0; k < 3; k++) {
f[i][j][k] = -142857;
}
}
}
cnt = 0;
}
void standard() {
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 3; j++) {
for (int k = 0; k < 3; k++) {
if (f[i][j][k] < 0) {
f[i][j][k] = -142857;
}
f[i][j][k] = min(f[i][j][k], 4);
}
}
}
}
bool operator < (Status b) const {
for (int i = 0; i < 2; i++) {
for (int j = 0; j <= 2; j++) {
for (int k = 0; k <= 2; k++) {
if (f[i][j][k] ^ b.f[i][j][k]) {
return f[i][j][k] < b.f[i][j][k];
}
}
}
}
return cnt < b.cnt;
}
bool valid() {
if (cnt >= 7) {
return false;
}
for (int j = 0; j <= 2; j++) {
for (int k = 0; k <= 2; k++) {
if (f[1][j][k] >= 4) {
return false;
}
}
}
return true;
}
Status extend(int c) {
Status nf;
nf.reset();
nf.cnt = cnt + (c >= 2);
for (int j = 0; j <= 2; j++) {
for (int k = 0; k <= 2; k++) {
for (int t = 0; t <= j && t <= c; t++) {
int nj = min(k + j - t, min(2, c - t));
int nk = min(2, c - t - nj);
vmax(nf.f[0][nj][nk], f[0][j][k] + t);
vmax(nf.f[1][nj][nk], f[1][j][k] + t);
}
if (c >= 3) {
for (int t = 0; t <= j && t <= c - 3; t++) {
int nj = min(k + j - t, min(2, c - 3 - t));
int nk = c - t - nj - 3;
vmax(nf.f[0][nj][nk], f[0][j][k] + t + 1);
vmax(nf.f[1][nj][nk], f[1][j][k] + t + 1);
}
}
if (c >= 2) {
for (int t = 0; t <= j && t <= c - 2; t++) {
int nj = min(k + j - t, min(2, c - t - 2));
int nk = c - t - nj - 2;
vmax(nf.f[1][nj][nk], f[0][j][k] + t);
}
}
}
}
nf.standard();
return nf;
}
void log() {
cerr <<"--- ";
cerr << cnt << "\n";
for (int i = 0; i <= 2; i++) {
for (int j = 0; j <= 2; j++) {
cerr << f[1][i][j] << " ";
}
cerr << '\n';
}
cerr << '\n';
cerr << "----\n";
}
} Status;
const int S = 4000;
const Zi comb[5][5] = {{1}, {1, 1}, {1, 2, 1}, {1, 3, 3, 1}, {1, 4, 6, 4, 1}};
int cnts = 0;
map<Status, int> Gs;
int tr[S][5];
int dfs(Status s) {
if (Gs.count(s)) {
return Gs[s];
}
int x = cnts++;
assert(cnts < S);
Gs[s] = x;
for (int c = 0; c <= 4; c++) {
Status t = s.extend(c);
if (t.valid()) {
tr[x][c] = dfs(t);
} else {
tr[x][c] = -1;
}
}
return x;
}
int n;
int cnt[105];
Zi f[2][405][S];
int main() {
Status s0;
s0.reset();
s0.f[0][0][0] = 0;
dfs(s0);
scanf("%d", &n);
for (int i = 1, x; i <= 13; i++) {
scanf("%d%*d", &x);
cnt[x]++;
}
f[0][0][0] = 1;
int cur = 0;
for (int i = 1; i <= n; i++) {
memset(f[cur ^= 1], 0, sizeof(f[0]));
for (int j = 0; j <= (i - 1) * 4; j++) {
for (int s = 0; s < cnts; s++) {
Zi v = f[cur ^ 1][j][s];
if (!v.v) {
continue;
}
int hc = cnt[i];
for (int c = 0; c + hc < 5; c++) {
int ns = tr[s][c + hc];
if (~ns) {
f[cur][j + c][ns] += v * comb[4 - hc][c];
}
}
}
}
}
int T = 4 * n - 13;
vector<Zi> fac (T + 1);
fac[0] = 1;
for (int i = 1; i <= T; i++) {
fac[i] = fac[i - 1] * i;
}
Zi ans = 0;
for (int i = 0; i < T; i++) {
Zi sum = 0;
for (int s = 0; s < cnts; s++) {
sum += f[cur][i][s];
}
ans += sum * fac[i] * fac[T - i];
}
ans *= ~fac[T];
printf("%d\n", ans.v);
return 0;
}
Problem B 线段树
考虑一个点的贡献,问题相当于问有多少操作集合使得它的 $tg$ 为 1.考虑它到根的链上,显然只用记录三种状态:没有点有标记,它没有标记但其中某个点有,它有标记。然后修改相当于单点乘某个矩阵或者区间乘某个矩阵。
Code
#include <bits/stdc++.h>
using namespace std;
typedef bool boolean;
#define ll long long
void exgcd(int a, int b, int& x, int& y) {
if (!b) {
x = 1, y = 0;
} else {
exgcd(b, a % b, y, x);
y -= (a / b) * x;
}
}
int inv(int a, int n) {
int x, y;
exgcd(a, n, x, y);
return (x < 0) ? (x + n) : (x);
}
const int Mod = 998244353;
template <const int Mod = :: Mod>
class Z {
public:
int v;
Z() : v(0) { }
Z(int x) : v(x){ }
Z(ll x) : v(x % Mod) { }
friend Z operator + (const Z& a, const Z& b) {
int x;
return Z(((x = a.v + b.v) >= Mod) ? (x - Mod) : (x));
}
friend Z operator - (const Z& a, const Z& b) {
int x;
return Z(((x = a.v - b.v) < 0) ? (x + Mod) : (x));
}
friend Z operator * (const Z& a, const Z& b) {
return Z(a.v * 1ll * b.v);
}
friend Z operator ~(const Z& a) {
return inv(a.v, Mod);
}
friend Z operator - (const Z& a) {
return Z(0) - a;
}
Z& operator += (Z b) {
return *this = *this + b;
}
Z& operator -= (Z b) {
return *this = *this - b;
}
Z& operator *= (Z b) {
return *this = *this * b;
}
friend boolean operator == (const Z& a, const Z& b) {
return a.v == b.v;
}
};
Z<> qpow(Z<> a, int p) {
Z<> rt = Z<>(1), pa = a;
for ( ; p; p >>= 1, pa = pa * pa) {
if (p & 1) {
rt = rt * pa;
}
}
return rt;
}
typedef Z<> Zi;
const int N = 1e5 + 5;
typedef class Matrix {
public:
Zi a[3][3];
Matrix() {
memset(a, 0, sizeof(a));
}
Matrix operator + (Matrix b) {
Matrix rt;
#define _(x, y) rt[x][y] = a[x][y] + b[x][y];
_(0, 0) _(0, 1) _(0, 2) _(1, 0) _(1, 1) _(1, 2) _(2, 0) _(2, 1) _(2, 2)
#undef _
return rt;
}
Matrix operator - (Matrix b) {
Matrix rt;
#define _(x, y) rt[x][y] = a[x][y] - b[x][y];
_(0, 0) _(0, 1) _(0, 2) _(1, 0) _(1, 1) _(1, 2) _(2, 0) _(2, 1) _(2, 2)
#undef _
return rt;
}
Matrix operator * (Matrix b) {
Matrix rt;
#define _(x, y) rt[x][y] = a[x][0] * b[0][y] + a[x][1] * b[1][y] + a[x][2] * b[2][y];
_(0, 0) _(0, 1) _(0, 2) _(1, 0) _(1, 1) _(1, 2) _(2, 0) _(2, 1) _(2, 2)
#undef _
return rt;
}
Zi* operator [] (int p) {
return a[p];
}
} Matrix;
typedef class SegTreeNode {
public:
bool has_tg;
Matrix v, s, tg;
SegTreeNode *l, *r;
void upd(Matrix t) {
v = v * t;
s = s * t;
if (has_tg) {
tg = tg * t;
} else {
tg = t;
has_tg = true;
}
}
void upd(Matrix t, Matrix t1) {
s = (s - v) * t1;
v = v * t;
s = s + v;
if (has_tg) {
tg = tg * t1;
} else {
tg = t1;
has_tg = true;
}
}
void _upd(Matrix x) {
s = s - v;
v = v * x;
s = s + v;
}
void push_down() {
if (has_tg) {
l->upd(tg);
r->upd(tg);
has_tg = false;
}
}
void push_up() {
s = l->s + v + r->s;
}
} SegTreeNode;
Matrix I, m1, m2, m3, m4, m5;
void prepare_matrix() {
I[0][0] = I[1][1] = I[2][2] = 1;
m1 = m2 = m3 = m4 = I;
m1[0][0] += 1, m1[1][0] += 1, m1[2][0] += 1;
m2[0][2] += 1, m2[1][2] += 1, m2[2][2] += 1;
m3[0][1] += 1, m3[1][1] += 1, m3[2][2] += 1;
m4[0][0] += 1, m4[1][2] += 1, m4[2][2] += 1;
m5 = I + I;
}
typedef class SegmentTree {
public:
static SegTreeNode pool[N << 1];
static SegTreeNode* top;
static SegTreeNode* newnode() {
top->v[0][0] = 1;
top->has_tg = false;
return top++;
}
int n, low;
SegTreeNode* rt;
SegmentTree() : rt(NULL) { }
void build(SegTreeNode*& p, int l, int r) {
p = newnode();
if (l ^ r) {
int mid = (l + r) >> 1;
build(p->l, l, mid);
build(p->r, mid + 1, r);
p->push_up();
} else {
p->s = p->v;
}
}
void build(int n, int low = 1) {
this->n = n;
this->low = low;
build(rt, low, n);
}
void modify(SegTreeNode* p, int l, int r, int ql, int qr) {
if (l == ql && r == qr) {
p->upd(m2, m3);
return;
}
int mid = (l + r) >> 1;
p->push_down();
p->v = p->v * m1;
if (qr <= mid) {
modify(p->l, l, mid, ql, qr);
p->r->upd(m4, m5);
} else if (ql > mid) {
modify(p->r, mid + 1, r, ql, qr);
p->l->upd(m4, m5);
} else {
modify(p->l, l, mid, ql, mid);
modify(p->r, mid + 1, r, mid + 1, qr);
}
p->push_up();
}
void modify(int l, int r) {
assert(l >= low && r <= n);
assert(l <= r);
modify(rt, low, n, l, r);
}
Zi query() {
return rt->s[0][2];
}
} SegmentTree;
SegTreeNode SegmentTree :: pool[N << 1];
SegTreeNode* SegmentTree :: top = SegmentTree :: pool;
int n, m;
SegmentTree st;
int main() {
prepare_matrix();
scanf("%d%d", &n, &m);
st.build(n);
int op, x, y;
while (m--) {
scanf("%d", &op);
if (op == 1) {
scanf("%d%d", &x, &y);
st.modify(x, y);
} else {
printf("%d\n", st.query().v);
}
}
return 0;
}
Problem C Minimax 搜索
假设最终根的权值 $x$ 。首先如果 $S$ 中包含 $x$,显然答案为 1,接下来不再考虑。
否则考虑计算答案 $ > k$ 的方案数,不难发现只用考虑将根的权值变为 $x + 1$ 或者 $x - 1$。假设是变为 $x + 1$,那么将 $\leqslant x$ 中能够修改的都改为 $x + 1$。然后将小于等于 $x$ 的记为 0,大于的记为 1,判断根节点的权值是否是 0。不难发现不能变为 $x + 1$ 和不能变为 $x - 1$ 的情况是独立,只用将它们的方案数乘起来。
然后按某种顺序使得某些点可以修改,然后 ddp 维护一下就行了。
Code
#include <bits/stdc++.h>
using namespace std;
typedef bool boolean;
#define ll long long
void exgcd(int a, int b, int& x, int& y) {
if (!b) {
x = 1, y = 0;
} else {
exgcd(b, a % b, y, x);
y -= (a / b) * x;
}
}
int inv(int a, int n) {
int x, y;
exgcd(a, n, x, y);
return (x < 0) ? (x + n) : (x);
}
const int Mod = 998244353;
template <const int Mod = :: Mod>
class Z {
public:
int v;
Z() : v(0) { }
Z(int x) : v(x){ }
Z(ll x) : v(x % Mod) { }
friend Z operator + (const Z& a, const Z& b) {
int x;
return Z(((x = a.v + b.v) >= Mod) ? (x - Mod) : (x));
}
friend Z operator - (const Z& a, const Z& b) {
int x;
return Z(((x = a.v - b.v) < 0) ? (x + Mod) : (x));
}
friend Z operator * (const Z& a, const Z& b) {
return Z(a.v * 1ll * b.v);
}
friend Z operator ~(const Z& a) {
return inv(a.v, Mod);
}
friend Z operator - (const Z& a) {
return Z(0) - a;
}
Z& operator += (Z b) {
return *this = *this + b;
}
Z& operator -= (Z b) {
return *this = *this - b;
}
Z& operator *= (Z b) {
return *this = *this * b;
}
friend boolean operator == (const Z& a, const Z& b) {
return a.v == b.v;
}
};
Z<> qpow(Z<> a, int p) {
Z<> rt = Z<>(1), pa = a;
for ( ; p; p >>= 1, pa = pa * pa) {
if (p & 1) {
rt = rt * pa;
}
}
return rt;
}
typedef Z<> Zi;
const int N = 2e5 + 5;
typedef class Matrix {
public:
Zi a[2][2];
void reset() {
memset(a, 0, sizeof(a));
}
void setI() {
a[0][0] = a[1][1] = 1;
}
Zi* operator [] (int p) {
return a[p];
}
Matrix operator * (Matrix b) {
Matrix rt;
#define G(x, y) rt[x][y] = a[x][0] * b[0][y] + a[x][1] * b[1][y];
G(0, 0) G(0, 1) G(1, 0) G(1, 1);
#undef G
return rt;
}
} Matrix;
typedef class SegTreeNode {
public:
Matrix a;
SegTreeNode *l, *r, *fa;
void push_up() {
a = l->a * r->a;
}
} SegTreeNode;
SegTreeNode pool[N << 2];
SegTreeNode *_top;
SegTreeNode* newnode() {
_top->a.reset();
_top->l = _top->r = _top->fa = NULL;
return _top++;
}
int n, _L, _R;
Zi ans[N];
int us[N], vs[N], dep[N];
vector<int> G[N];
int sz[N], zson[N], fa[N];
int dfs(int p, int fa) {
::fa[p] = fa;
dep[p] = dep[fa] + 1;
if (fa) {
G[p].erase(find(G[p].begin(), G[p].end(), fa));
}
int rt = (G[p].empty()) ? (p) : ((dep[p] & 1) ? 0 : (n + 1));
sz[p] = 1;
for (auto e : G[p]) {
int tmp = dfs(e, p);
rt = (dep[p] & 1) ? max(rt, tmp) : min(rt, tmp);
sz[p] += sz[e];
if (sz[e] > sz[zson[p]]) {
zson[p] = e;
}
}
return rt;
}
SegTreeNode *rt1[N], *rt2[N];
int top[N], bot[N], stk[N], tp;
void build(SegTreeNode* &p, int l, int r, SegTreeNode** ar, const vector<int>& v) {
if (l > r) {
return;
}
p = newnode();
p->a.setI();
if (l == r) {
ar[v[l]] = p;
return;
}
int mid = (l + r) >> 1;
build(p->l, l, mid, ar, v);
build(p->r, mid + 1, r, ar, v);
p->l->fa = p->r->fa = p;
}
void build() {
for (int i = 1; i <= tp; i++) {
top[stk[i]] = stk[tp];
bot[stk[i]] = stk[1];
}
SegTreeNode* prt;
build(prt, 0, tp - 1, rt1, vector<int>(stk + 1, stk + tp + 1));
for (int i = 1; i <= tp; i++) {
int p = stk[i];
vector<int> ch = G[p];
if (zson[p]) {
ch.erase(find(ch.begin(), ch.end(), zson[p]));
}
build(prt, 0, (signed) ch.size() - 1, rt2, ch);
}
tp = 0;
}
void dfs(int p) {
if (!G[p].empty()) {
for (auto e : G[p]) {
if (e ^ zson[p]) {
dfs(e);
build();
}
}
dfs(zson[p]);
}
stk[++tp] = p;
}
Matrix pack(int p, Zi f0, Zi f1) { // p is the father
Matrix rt;
memset(rt.a, 0, sizeof(rt.a));
if (dep[p] & 1) {
rt[0][0] = f0, rt[0][1] = f1;
rt[1][1] = f1 + f0;
} else {
rt[0][0] = f0 + f1;
rt[1][0] = f0, rt[1][1] = f1;
}
return rt;
}
Zi modify(int x, Zi f0, Zi f1, int _ = 0) {
Matrix a;
memset(a.a, 0, sizeof(a.a));
a[0][0] = f0, a[0][1] = f1;
SegTreeNode* p;
while (x) {
p = rt1[x];
p->a = a;
for ( ; p->fa; (p = p->fa)->push_up());
x = top[x];
if (x == 1) {
return p->a[0][_];
}
a = pack(fa[x], p->a[0][0], p->a[0][1]);
p = rt2[x];
p->a = a;
for ( ; p->fa; (p = p->fa)->push_up());
a = p->a;
x = fa[x];
}
assert(false);
return a[0][0];
}
int xrt, cntleaf = 0;
vector<Zi> work1() {
vector<int> leaf;
Zi rest = 1;
for (int i = 1; i <= n; i++) {
if (G[i].empty()) {
modify(i, (i <= xrt), (i > xrt));
if (i < xrt) {
leaf.push_back(i);
++cntleaf;
rest = rest + rest;
}
}
}
sort(leaf.begin(), leaf.end(), greater<int>());
vector<Zi> f (n);
auto it = leaf.begin(), _it = leaf.end();
Zi cf = rest, inv2 = (Mod + 1) >> 1;
for (int d = 0; d < n; d++) {
while (it != _it && *it >= xrt - d + 1) {
cf = modify(*it, 1, 1) * (rest *= inv2);
it++;
}
f[d] = cf;
}
return f;
}
vector<Zi> work2() {
vector<int> leaf;
Zi rest = 1;
for (int i = 1; i <= n; i++) {
if (G[i].empty()) {
modify(i, (i < xrt), (i >= xrt));
if (i > xrt) {
leaf.push_back(i);
++cntleaf;
rest = rest + rest;
}
}
}
sort(leaf.begin(), leaf.end());
vector<Zi> f (n);
auto it = leaf.begin(), _it = leaf.end();
Zi cf = rest, inv2 = (Mod + 1) >> 1;
for (int d = 0; d < n; d++) {
while (it != _it && *it <= xrt + d - 1) {
cf = modify(*it, 1, 1, 1) * (rest *= inv2);
it++;
}
f[d] = cf;
}
return f;
}
int main() {
scanf("%d%d%d", &n, &_L, &_R);
for (int i = 1, u, v; i < n; i++) {
scanf("%d%d", &u, &v);
G[u].push_back(v);
G[v].push_back(u);
}
_top = pool;
xrt = dfs(1, 0);
dfs(1);
build();
auto a = work1();
auto b = work2();
for (int i = 0; i < n; i++) {
ans[i + 1] = a[i] * b[i];
}
for (int i = 1; i <= n; i++) {
ans[i] = ans[i] - ans[i + 1];
}
ans[1] += qpow(2, cntleaf);
ans[n] -= 1;
for (int i = _L; i <= _R; i++) {
printf("%d%c", ans[i].v, (i == _R) ? ('\n') : ' ');
}
return 0;
}
Round 2
Problem A 开关
不考虑第一次的话,不难用 egf 表示出经过 $i$ 步从初始状态回到全 0 的生成函数是 $F(x) = 2^{-n}\prod_i [e^{p_ix} + (-1)^{s_i}e^{-p_i x}] $。
设从某个状态经过 $i$ 步回到它本身状态的 egf 是 $G(x) = 2^{-n}\prod_i [e^{p_ix} + (-1)^{s_i}e^{-p_i x}]$ 。
设它们的 ogf 形式分别是 $f(x), g(x)$,设走 $i$ 步第一次回到全 0 的生成函数为 $h(x)$。
显然有 $h(x) g(x) = f(x)$。注意到 $h'(1)$ 即为答案。考虑怎么求 $f, g$,注意到 $F(x) = \sum_t a_t e^{t x}$,这里可以直接把 $e^{t x}$ 换成 $\frac{1}{1 - tx}$ 来得到 ogf。
现在的问题变成计算 $\left ( \frac{f(x)}{g(x)} \right )'\mid_{x = 1}$。你注意到分子分母都包含 $\frac{1}{1 - x}$,分子分母同乘 $(1 - x)$ 再套用求导的公式。 然后手算一下 $\left ( \frac{1 - x}{1 - kx} \right )'\mid_{x = 1} = \frac{1}{k - 1}$。
剩下只用做一个暴力背包就行了。
Code
#include <bits/stdc++.h>
using namespace std;
typedef bool boolean;
#define ll long long
void exgcd(int a, int b, int& x, int& y) {
if (!b) {
x = 1, y = 0;
} else {
exgcd(b, a % b, y, x);
y -= (a / b) * x;
}
}
int inv(int a, int n) {
int x, y;
exgcd(a, n, x, y);
return (x < 0) ? (x + n) : (x);
}
const int Mod = 998244353;
template <const int Mod = :: Mod>
class Z {
public:
int v;
Z() : v(0) { }
Z(int x) : v(x){ }
Z(ll x) : v(x % Mod) { }
friend Z operator + (const Z& a, const Z& b) {
int x;
return Z(((x = a.v + b.v) >= Mod) ? (x - Mod) : (x));
}
friend Z operator - (const Z& a, const Z& b) {
int x;
return Z(((x = a.v - b.v) < 0) ? (x + Mod) : (x));
}
friend Z operator * (const Z& a, const Z& b) {
return Z(a.v * 1ll * b.v);
}
friend Z operator ~(const Z& a) {
return inv(a.v, Mod);
}
friend Z operator - (const Z& a) {
return Z(0) - a;
}
Z& operator += (Z b) {
return *this = *this + b;
}
Z& operator -= (Z b) {
return *this = *this - b;
}
Z& operator *= (Z b) {
return *this = *this * b;
}
friend boolean operator == (const Z& a, const Z& b) {
return a.v == b.v;
}
};
Z<> qpow(Z<> a, int p) {
Z<> rt = Z<>(1), pa = a;
for ( ; p; p >>= 1, pa = pa * pa) {
if (p & 1) {
rt = rt * pa;
}
}
return rt;
}
typedef Z<> Zi;
const int N = 105, V = 5e4 + 3;
int n, sp;
int s[N], p[N];
Zi f[V], g[V];
Zi calc(Zi* f) {
Zi ret = 0;
for (int i = 0; i < sp; i++) {
ret += ~(Zi(i) - sp) * f[i];
}
return ret * sp * ((Mod + 1) >> 1);
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d", s + i);
}
for (int i = 1; i <= n; i++) {
scanf("%d", p + i);
}
f[0] = g[0] = 1;
for (int i = 1; i <= n; i++) {
sp += p[i];
for (int j = sp; j >= p[i]; j--) {
g[j] += g[j - p[i]];
f[j] = ((s[i]) ? (-f[j]) : f[j]) + f[j - p[i]];
}
if (s[i]) {
for (int j = 0; j < p[i]; j++) {
f[j] = -f[j];
}
}
}
Zi ans = calc(f) - calc(g);
printf("%d\n", ans.v);
return 0;
}
Problem B 语言
启发式合并维护链并。
Code
#include <bits/stdc++.h>
using namespace std;
typedef bool boolean;
typedef multiset<int>::iterator iter;
const int N = 1e5 + 5, N2 = N << 1;
template <typename T>
class SparseTable {
public:
int n;
vector<T> Log2;
vector<vector<T>> f;
function<boolean(T, T)> compare;
void init(int n, T* a, const function<boolean(T, T)>& compare = less<T>()) {
this->n = n;
this->compare = compare;
Log2.resize(n + 1);
Log2[0] = -1;
for (int i = 1; i <= n; i++) {
Log2[i] = Log2[i >> 1] + 1;
}
f.resize(Log2[n] + 1, vector<T>(n + 1));
for (int i = 1; i <= n; i++) {
f[0][i] = a[i];
}
for (int i = 1; i <= Log2[n]; i++) {
for (int j = 1; j + (1 << i) - 1 <= n; j++) {
T& x = f[i - 1][j], &y = f[i - 1][j + (1 << (i - 1))];
if (compare(x, y)) {
f[i][j] = x;
} else {
f[i][j] = y;
}
}
}
}
T query(int l, int r) {
int b = Log2[(r - l + 1)];
T& x = f[b][l], &y = f[b][r - (1 << b) + 1];
return compare(x, y) ? x : y;
}
};
int n, m;
int dfc;
vector<int> G[N];
SparseTable<int> st;
int edep[N2], ein[N2], eout[N2], tour[N2];
int edist(int x, int y) { // euler order
if (x > y) {
swap(x, y);
}
return edep[x] + edep[y] - (edep[st.query(x, y)] << 1);
}
int lca(int x, int y) {
x = ein[x], y = eout[y];
if (x > y) {
swap(x, y);
}
return tour[st.query(x, y)];
}
void dfs(int p, int fa, int d) {
edep[++dfc] = d;
tour[dfc] = p;
ein[p] = dfc;
for (auto e : G[p]) {
if (e ^ fa) {
dfs(e, p, d + 1);
edep[++dfc] = d;
tour[dfc] = p;
}
}
eout[p] = dfc;
}
typedef class VirtualTree {
public:
int length;
multiset<int> P;
iter prefix(iter x) {
return x == P.begin() ? --P.end() : --x;
}
iter suffix(iter x) {
++x;
return x == P.end() ? P.begin() : x;
}
void insert(int p) {
iter x = P.insert(p);
iter pr = prefix(x);
iter sf = suffix(x);
length -= edist(*pr, *sf);
length += edist(*pr, p);
length += edist(p, *sf);
}
void remove(int p) {
iter x = P.find(p);
iter pr = prefix(x);
iter sf = suffix(x);
length -= edist(*pr, p);
length -= edist(p, *sf);
length += edist(*pr, *sf);
P.erase(x);
}
int size() {
return P.size();
}
} VirtualTree;
VirtualTree _vt[N];
VirtualTree *vt[N];
vector<int> Vi[N], Vo[N];
long long ans = 0;
void merge(int x, int y) {
if (vt[x]->size() < vt[y]->size()) {
swap(vt[x], vt[y]);
}
for (auto p : vt[y]->P) {
vt[x]->insert(p);
}
}
void solve(int p, int fa) {
for (auto e : G[p]) {
if (e ^ fa) {
solve(e, p);
merge(p, e);
}
}
for (auto x : Vi[p]) {
vt[p]->insert(x);
}
ans += vt[p]->length;
for (auto x : Vo[p]) {
vt[p]->remove(x);
vt[p]->remove(x);
}
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1, u, v; i < n; i++) {
scanf("%d%d", &u, &v);
G[u].push_back(v);
G[v].push_back(u);
}
for (int i = 1; i <= n; i++) {
vt[i] = _vt + i;
}
dfs(1, 0, 0);
int* tmp = new int[(dfc + 1)];
for (int i = 0; i <= dfc; i++) {
tmp[i] = i;
}
st.init(dfc, tmp, [&] (int x, int y) { return edep[x] < edep[y]; });
for (int i = 1, u, v; i <= m; i++) {
scanf("%d%d", &u, &v);
Vi[u].push_back(ein[u]);
Vi[u].push_back(ein[v]);
Vi[v].push_back(ein[u]);
Vi[v].push_back(ein[v]);
int g = lca(u, v);
Vo[g].push_back(ein[u]);
Vo[g].push_back(ein[v]);
}
solve(1, 0);
ans >>= 2;
printf("%lld\n", ans);
return 0;
}
Problem C 浙江省选
考虑最终能成为 rk 1 的是直接半平交。考虑求最终能成为 rk 2 的,把剩下的求半平面交,把剩下可能成为 rk 1 的判断一下它出现在凸包上的区间是否被本来是 rk 1 的覆盖。具体地来说你可以用之前是 rk 1 的直线,二分一下它覆盖了凸包上哪些区间(显然这样的区间只有常数个)。这样就能找到所有能成为 rk 2 的直线。对于 rk 更大的情况只用变更为计算被覆盖的次数就行了。
注意坐标必须是整数,以及 long long 的运算可能会炸 long long。
Code
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
typedef long long ll;
typedef __int128 LL;
const ll llf = (signed long long) (~0ull >> 2);
typedef class Fraction {
public:
ll a, b;
Fraction() { }
Fraction(ll x, ll y) : a(x), b(y) {
if (b < 0) {
b = -b;
a = -a;
}
}
bool operator < (Fraction x) const {
return ((LL) a) * x.b < ((LL) b) * x.a;
}
bool operator <= (Fraction x) const {
return ((LL) a) * x.b <= ((LL) b) * x.a;
}
bool operator == (Fraction x) const {
return ((LL) a) * x.b == ((LL) b) * x.a;
}
ll floor() {
return a < 0 ? ((a - b + 1) / b) : (a / b);
}
ll ceil() {
return a < 0 ? (a / b) : ((a + b - 1) / b);
}
} Fraction;
typedef class Line {
public:
ll a, b;
int id;
Fraction intersect(Line x) {
assert(a != x.a);
return Fraction(x.b - b, a - x.a);
}
void read() {
scanf("%lld%lld", &a, &b);
}
} Line;
typedef class Event {
public:
int op, v; // 0 : insert, 1 : cover
Fraction p;
Event(int op, int v, Fraction p) : op(op), v(v), p(p) { }
bool operator < (Event b) const {
return p == b.p ? (op == b.op ? v > b.v : op < b.op) : p < b.p;
}
} Event;
int n, m;
Line li[N];
Fraction fl[N], fr[N];
bool ban[N];
int ans[N];
Line stk[N];
vector<Event> E;
vector<int> ncand;
vector<int> candidate;
const Fraction f0 (0, 1);
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) {
li[i].read();
li[i].id = i;
ans[i] = -1;
}
sort(li + 1, li + n + 1, [&] (Line a, Line b) { return a.a == b.a ? a.b > b.b : a.a < b.a; });
for (int r = 1; r <= m; r++) {
int tp = 0;
ll lsa = 0;
fill(ban + 1, ban + n + 1, false);
for (int i = 1; i <= n; i++) {
if (ans[li[i].id] == -1 && li[i].a != lsa) {
lsa = li[i].a;
while (tp >= 2 && stk[tp].intersect(li[i]) < stk[tp - 1].intersect(stk[tp]))
tp--;
stk[++tp] = li[i];
} else if (li[i].a == lsa) {
ban[i] = true;
}
}
if (!tp) {
break;
}
E.clear();
int cov = 0;
for (int i = 1; i <= n; i++) {
if (ban[i]) {
continue;
}
int l = 2, r = tp, mid;
Line& lc = li[i];
int id = li[i].id;
while (l <= r) {
mid = (l + r) >> 1;
if (stk[mid].a < lc.a && stk[mid].intersect(stk[mid - 1]) < lc.intersect(stk[mid])) {
l = mid + 1;
} else {
r = mid - 1;
}
}
--l;
if (stk[l].a >= lc.a) {
fl[i] = Fraction(-llf, 1);
} else {
fl[i] = stk[l].intersect(lc);
}
l = 1, r = tp - 1;
while (l <= r) {
mid = (l + r) >> 1;
if (stk[mid].a > lc.a && lc.intersect(stk[mid]) < stk[mid].intersect(stk[mid + 1])) {
r = mid - 1;
} else {
l = mid + 1;
}
}
++r;
if (stk[r].a <= lc.a) {
fr[i] = Fraction(llf, 1);
} else {
fr[i] = lc.intersect(stk[r]);
}
if (fr[i] < fl[i] || fr[i] < f0) {
continue;
}
if (ans[id] != -1) {
if (fl[i] < f0) {
cov++;
} else {
E.emplace_back(1, 1, fl[i]);
}
if (fr[i].a != llf) {
E.emplace_back(1, -1, fr[i]);
}
} else {
E.emplace_back(0, i, fl[i]);
}
}
sort(E.begin(), E.end());
Fraction lsf = f0;
bool have_first = cov < r;
for (auto it = E.begin(), nit = it, _it = E.end(); it != _it; it = nit) {
while (nit != _it && (*nit).p == (*it).p) {
auto e = *nit;
if (!e.op) {
candidate.push_back(e.v);
} else {
cov += e.v;
}
nit++;
}
auto p = (*it).p;
if (cov < r) {
if (!have_first) {
have_first = true;
lsf = p;
}
} else if (have_first) {
if (p.floor() >= lsf.ceil()) {
ncand.clear();
for (auto x : candidate) {
auto L = max(fl[x], lsf);
auto R = min(fr[x], p);
if (L.ceil() <= R.floor()) {
ans[li[x].id] = r;
} else if (lsf < fl[x] && p <= fr[x]) {
ncand.push_back(x);
}
}
candidate = ncand;
}
have_first = false;
}
}
if (cov < r) {
for (auto x : candidate) {
auto L = max(fl[x], lsf);
auto R = fr[x];
if (L.ceil() <= R.floor()) {
ans[li[x].id] = r;
}
}
}
candidate.clear();
}
for (int i = 1; i <= n; i++) {
printf("%d%c", ans[i], " \n"[i == n]);
}
return 0;
}
浙公网安备 33010602011771号