@qq290637843
2020-11-17T15:37:55.000000Z
字数 23373
阅读 442
官方题解如下。
关于改写法正确性的证明:
考虑如下两个向线性基里插入向量的算法:
1、
void push(std::pair<ul, ul> base[], std::pair<ul, ul> p){for (ul i = 29; ~i && p.first; --i) {if (~p.first >> i & 1) {continue;}if (!base[i].first) {base[i] = p;break;}p.first ^= base[i].first;}}
2、
void push(std::pair<ul, ul> base[], std::pair<ul, ul> p){for (ul i = 29; ~i && p.first; --i) {if (~p.first >> i & 1) {continue;}if (!base[i].first) {base[i] = p;break;}if (p.second > base[i].second) {std::swap(p, base[i]);}p.first ^= base[i].first;}}
于是证明分为两个部分:一、证明将区间中的向量从右向左用算法1插入得到的线性基能解决的询问;二、证明将区间中的向量从左向右用算法2插入得到的线性基,并将其中下标小于的向量删去得到的结果,正好和第一部分中所说的线性基一致。
#include <bits/stdc++.h>using ul = std::uint32_t;using li = std::int32_t;using ull = std::uint64_t;using ll = std::int64_t;const ul maxn = 1e5;ul T;std::pair<ul, ul> base[maxn + 1][30];ul n, q;ul pre[maxn + 1];ul p[maxn + 1];void push(std::pair<ul, ul> base[], std::pair<ul, ul> p){for (ul i = 29; ~i && p.first; --i) {if (~p.first >> i & 1) {continue;}if (!base[i].first) {base[i] = p;break;}if (p.second > base[i].second) {std::swap(p, base[i]);}p.first ^= base[i].first;}}std::string instr;std::stringstream iss;std::stringstream oss;int main(){std::ios::sync_with_stdio(false);std::cin.tie(0);iss.tie(0);oss.tie(0);std::getline(std::cin, instr, char(0));iss.str(instr);iss >> T;for (ul Case = 1; Case <= T; ++Case) {iss >> n >> q;for (ul i = 1; i <= n; ++i) {ul m;iss >> m >> p[i];std::memcpy(base[i], base[i - 1], sizeof(base[i]));pre[i] = 0;for (ul j = 1; j <= m; ++j) {ul x, y;iss >> x >> y;push(base[i], std::pair<ul, ul>(x ^ y, i));pre[i] ^= x;}pre[i] ^= pre[i - 1];}for (ul i = 1; i <= q; ++i) {ul t;iss >> t;if (t == 1) {ul x, y;iss >> x >> y;p[x] = y;} else {ul l, r, x;iss >> l >> r >> x;x ^= pre[r] ^ pre[l - 1];for (ul i = 29; ~i; --i) {if (base[r][i].first == 0 || base[r][i].second < l) {continue;}if ((base[r][i].first ^ x ^ p[base[r][i].second]) < (x ^ p[base[r][i].second])) {x ^= base[r][i].first;}}oss << x << '\n';}}}std::cout << oss.str();return 0;}
解释一下这个官方题解是什么意思:
第一部分:
注意一个带颜色的序列合法当且仅当对任何位置上的项,其之后的第一个(如果有)和其异色的项的值必须小于等于的值。于是可知第一部分中所说的也是充要条件。
第二部分:
注意第一部分的充要条件可以得出,所有中的项的颜色是相同的。以及可知,染色有且只有两个限制:①中项颜色一致;②值相同的两项颜色相异。
第三部分:第一句话来自第二部分,于是。的意思是,所以第二句话就是说,注意这里的和之前的来源并不一致。考虑将合法无色序列先分为子段集族,那么可以给这些子段集黑白染色(注意,和红蓝染色是两件事,红蓝染色是每个子段集中对子段按照二分图那样染色,而这里是说每个子段集都染相同的颜色),那么有种染法,每个颜色的项目取出再将值压回和,于是正好对应了两个无色(指红蓝)的合法序列(这里只是说是映射,没说单满问题)。而反过来,正好对于每个,以及长为和的无色合法序列,从中选个数,将投上去,将投到剩下的个数,那么正好对应一种长为的黑白染色的合法序列。综上可知。
懒得整理了,把整个多项式的模板全复制过来了,实际上用不到这么多。
#include <bits/stdc++.h>using ul = std::uint32_t;using li = std::int32_t;using ull = std::uint64_t;using ll = std::int64_t;using vul = std::vector<ul>;std::mt19937_64 rnd;const ul base = 998244353;ul plus(ul a, ul b){return a + b < base ? a + b : a + b - base;}ul minus(ul a, ul b){return a < b ? a + base - b : a - b;}ul mul(ul a, ul b){return ull(a) * ull(b) % base;}void exgcd(li a, li b, li& x, li& y){if (b) {exgcd(b, a % b, y, x);y -= x * (a / b);} else {x = 1;y = 0;}}ul inverse(ul a){li x, y;exgcd(a, base, x, y);return x < 0 ? x + li(base) : x;}ul pow(ul a, ul b){ul ret = 1;ul temp = a;while (b) {if (b & 1) {ret = mul(ret, temp);}temp = mul(temp, temp);b >>= 1;}return ret;}ul sqrt(ul x){ul a;ul w2;while (true) {a = rnd() % base;w2 = minus(mul(a, a), x);if (pow(w2, base - 1 >> 1) == base - 1) {break;}}ul b = base + 1 >> 1;ul rs = 1, rt = 0;ul as = a, at = 1;ul qs, qt;while (b) {if (b & 1) {qs = plus(mul(rs, as), mul(mul(rt, at), w2));qt = plus(mul(rs, at), mul(rt, as));rs = qs;rt = qt;}b >>= 1;qs = plus(mul(as, as), mul(mul(at, at), w2));qt = plus(mul(as, at), mul(as, at));as = qs;at = qt;}return rs + rs < base ? rs : base - rs;}class polynomial : public vul {public:void clearzero() {while (size() && !back()) {pop_back();}}polynomial()=default;polynomial(const vul& a): vul(a) { }polynomial(vul&& a): vul(std::move(a)) { }ul degree() const {return size() - 1;}ul operator()(ul x) const {ul ret = 0;for (ul i = size() - 1; ~i; --i) {ret = mul(ret, x);ret = plus(ret, vul::operator[](i));}return ret;}};polynomial& operator+=(polynomial& a, const polynomial& b){a.resize(std::max(a.size(), b.size()), 0);for (ul i = 0; i != b.size(); ++i) {a[i] = plus(a[i], b[i]);}a.clearzero();return a;}polynomial operator+(const polynomial& a, const polynomial& b){polynomial ret = a;return ret += b;}polynomial& operator-=(polynomial& a, const polynomial& b){a.resize(std::max(a.size(), b.size()), 0);for (ul i = 0; i != b.size(); ++i) {a[i] = minus(a[i], b[i]);}a.clearzero();return a;}polynomial operator-(const polynomial& a, const polynomial& b){polynomial ret = a;return ret -= b;}class ntt_t {public:static const ul lgsz = 18;static const ul sz = 1 << lgsz;static const ul g = 3;ul w[sz + 1];ul leninv[lgsz + 1];ntt_t() {ul wn = pow(g, (base - 1) >> lgsz);w[0] = 1;for (ul i = 1; i <= sz; ++i) {w[i] = mul(w[i - 1], wn);}leninv[lgsz] = inverse(sz);for (ul i = lgsz - 1; ~i; --i) {leninv[i] = plus(leninv[i + 1], leninv[i + 1]);}}void operator()(vul& v, ul& n, bool inv) {ul lgn = 0;while ((1 << lgn) < n) {++lgn;}n = 1 << lgn;v.resize(n, 0);for (ul i = 0, j = 0; i != n; ++i) {if (i < j) {std::swap(v[i], v[j]);}ul k = n >> 1;while (k & j) {j &= ~k;k >>= 1;}j |= k;}for (ul lgmid = 0; (1 << lgmid) != n; ++lgmid) {ul mid = 1 << lgmid;ul len = mid << 1;for (ul i = 0; i != n; i += len) {for (ul j = 0; j != mid; ++j) {ul t0 = v[i + j];ul t1 = mul(w[inv ? (len - j << lgsz - lgmid - 1) : (j << lgsz - lgmid - 1)], v[i + j + mid]);v[i + j] = plus(t0, t1);v[i + j + mid] = minus(t0, t1);}}}if (inv) {for (ul i = 0; i != n; ++i) {v[i] = mul(v[i], leninv[lgn]);}}}} ntt;polynomial& operator*=(polynomial& a, const polynomial& b){if (!b.size() || !a.size()) {a.resize(0);return a;}polynomial temp = b;ul npmp1 = a.size() + b.size() - 1;if (ull(a.size()) * ull(b.size()) <= ull(npmp1) * ull(50)) {temp.resize(0);temp.resize(npmp1, 0);for (ul i = 0; i != a.size(); ++i) {for (ul j = 0; j != b.size(); ++j) {temp[i + j] = plus(temp[i + j], mul(a[i], b[j]));}}a = temp;a.clearzero();return a;}ntt(a, npmp1, false);ntt(temp, npmp1, false);for (ul i = 0; i != npmp1; ++i) {a[i] = mul(a[i], temp[i]);}ntt(a, npmp1, true);a.clearzero();return a;}polynomial operator*(const polynomial& a, const polynomial& b){polynomial ret = a;return ret *= b;}polynomial& operator*=(polynomial& a, ul b){if (!b) {a.resize(0);return a;}for (ul i = 0; i != a.size(); ++i) {a[i] = mul(a[i], b);}return a;}polynomial operator*(const polynomial& a, ul b){polynomial ret = a;return ret *= b;}polynomial inverse(const polynomial& a, ul lgdeg){polynomial ret({inverse(a[0])});polynomial temp;polynomial tempa;for (ul i = 0; i != lgdeg; ++i) {tempa.resize(0);tempa.resize(1 << i << 1, 0);for (ul j = 0; j != tempa.size() && j != a.size(); ++j) {tempa[j] = a[j];}temp = ret * (polynomial({2}) - tempa * ret);if (temp.size() > (1 << i << 1)) {temp.resize(1 << i << 1, 0);}temp.clearzero();std::swap(temp, ret);}return ret;}void quotientremain(const polynomial& a, polynomial b, polynomial& q, polynomial& r){if (a.size() < b.size()) {q = polynomial();r = std::move(a);return;}std::reverse(b.begin(), b.end());auto ta = a;std::reverse(ta.begin(), ta.end());ul n = a.size() - 1;ul m = b.size() - 1;ta.resize(n - m + 1);ul lgnmmp1 = 0;while ((1 << lgnmmp1) < n - m + 1) {++lgnmmp1;}q = ta * inverse(b, lgnmmp1);q.resize(n - m + 1);std::reverse(b.begin(), b.end());std::reverse(q.begin(), q.end());r = a - b * q;}polynomial mod(const polynomial& a, const polynomial& b){polynomial q, r;quotientremain(a, b, q, r);return r;}polynomial quotient(const polynomial& a, const polynomial& b){polynomial q, r;quotientremain(a, b, q, r);return q;}polynomial sqrt(const polynomial& a, ul lgdeg){polynomial ret({sqrt(a[0])});polynomial temp;polynomial tempa;for (ul i = 0; i != lgdeg; ++i) {tempa.resize(0);tempa.resize(1 << i << 1, 0);for (ul j = 0; j != tempa.size() && j != a.size(); ++j) {tempa[j] = a[j];}temp = (tempa * inverse(ret, i + 1) + ret) * (base + 1 >> 1);if (temp.size() > (1 << i << 1)) {temp.resize(1 << i << 1, 0);}temp.clearzero();std::swap(temp, ret);}return ret;}polynomial diffrential(const polynomial& a){if (!a.size()) {return a;}polynomial ret(vul(a.size() - 1, 0));for (ul i = 1; i != a.size(); ++i) {ret[i - 1] = mul(a[i], i);}return ret;}polynomial integral(const polynomial& a){polynomial ret(vul(a.size() + 1, 0));for (ul i = 0; i != a.size(); ++i) {ret[i + 1] = mul(a[i], inverse(i + 1));}return ret;}polynomial ln(const polynomial& a, ul lgdeg){polynomial da = diffrential(a);polynomial inva = inverse(a, lgdeg);polynomial ret = integral(da * inva);if (ret.size() > (1 << lgdeg)) {ret.resize(1 << lgdeg);ret.clearzero();}return ret;}polynomial exp(const polynomial& a, ul lgdeg){polynomial ret({1});polynomial temp;polynomial tempa;for (ul i = 0; i != lgdeg; ++i) {tempa.resize(0);tempa.resize(1 << i << 1, 0);for (ul j = 0; j != tempa.size() && j != a.size(); ++j) {tempa[j] = a[j];}temp = ret * (polynomial({1}) - ln(ret, i + 1) + tempa);if (temp.size() > (1 << i << 1)) {temp.resize(1 << i << 1, 0);}temp.clearzero();std::swap(temp, ret);}return ret;}polynomial pow(const polynomial& a, ul k, ul lgdeg){return exp(ln(a, lgdeg) * k, lgdeg);}polynomial alpi[1 << 16][17];polynomial getalpi(const ul x[], ul l, ul lgrml){if (lgrml == 0) {return alpi[l][lgrml] = vul({minus(0, x[l]), 1});}return alpi[l][lgrml] = getalpi(x, l, lgrml - 1) * getalpi(x, l + (1 << lgrml - 1), lgrml - 1);}void multians(const polynomial& f, const ul x[], ul y[], ul l, ul lgrml){if (f.size() <= 700) {for (ul i = l; i != l + (1 << lgrml); ++i) {y[i] = f(x[i]);}return;}if (lgrml == 0) {y[l] = f(x[l]);return;}multians(mod(f, alpi[l][lgrml - 1]), x, y, l, lgrml - 1);multians(mod(f, alpi[l + (1 << lgrml - 1)][lgrml - 1]), x, y, l + (1 << lgrml - 1), lgrml - 1);}const ul maxn = 1e5;ul T;ul n;polynomial tmp;std::stringstream iss;std::stringstream oss;std::string instr;int main(){rnd.seed(std::time(0));std::ios::sync_with_stdio(false);std::cin.tie(0);iss.tie(0);oss.tie(0);std::getline(std::cin, instr, char(0));iss.str(instr);tmp.resize(maxn + maxn + 1);tmp[0] = 1;for (ul i = 1; i <= maxn + maxn; ++i) {tmp[i] = mul(tmp[i - 1], plus(i, inverse(i)));}tmp = sqrt(tmp, 17);tmp.resize(maxn + 1);ul fac = 1;for (ul i = 1; i <= maxn; ++i) {fac = mul(i, fac);tmp[i] = mul(tmp[i], fac);}iss >> T;for (ul Case = 1; Case <= T; ++Case) {iss >> n;oss << tmp[n] << '\n';}std::cout << oss.str();return 0;}

#include <bits/stdc++.h>using ul = std::uint32_t;using li = std::int32_t;using ull = std::uint64_t;using ll = std::int64_t;ul T;ul t, a, c, m;ul plus(ul a, ul b){return a + b < m ? a + b : a + b - m;}ul mul(ul a, ul b){return ull(a) * ull(b) % m;}ull gcd(ull a, ull b){while (b) {b ^= a ^= b ^= a %= b;}return a;}const ul maxm = 1e6;ul to[maxm][21];ull cnt[maxm][21];ull ans;std::stringstream iss;std::stringstream oss;std::string instr;int main(){std::ios::sync_with_stdio(false);std::cin.tie(0);iss.tie(0);oss.tie(0);std::getline(std::cin, instr, char(0));iss.str(instr);iss >> T;for (ul Case = 1; Case <= T; ++Case) {iss >> t >> a >> c >> m;for (ul i = 0; i != m; ++i) {to[i][0] = plus(mul(a, plus(mul(a, i), c)), c);cnt[i][0] = ~i & 1;}ul maxstep = 0;for (ul sum = 0; sum <= t + t; ++sum) {ul step;if (sum & 1) {step = (std::min(sum, t + t - sum) - 1 >> 1) + 1;} else {step = std::min(sum, t + t - sum) >> 1;}maxstep = std::max(maxstep, step);}for (ul k = 1; (1 << k) <= maxstep; ++k) {for (ul i = 0; i != m; ++i) {to[i][k] = to[to[i][k - 1]][k - 1];cnt[i][k] = cnt[i][k - 1] + cnt[to[i][k - 1]][k - 1];}}ans = 0;for (ul sum = 0; sum <= t + t; ++sum) {if (sum & 1) {ul start = plus(mul(a, sum), c);ul step = (std::min(sum, t + t - sum) - 1 >> 1) + 1;while (step) {ul k = 31 - __builtin_clz(step);step -= 1 << k;ans += cnt[start][k] << 1;start = to[start][k];}} else {ul start = sum;ans += cnt[sum][0];start = to[sum][0];ul step = std::min(sum, t + t - sum) >> 1;while (step) {ul k = 31 - __builtin_clz(step);step -= 1 << k;ans += cnt[start][k] << 1;start = to[start][k];}}}ull mm = ull(t + 1) * ull(t + 1);ull g = gcd(ans, mm);oss << ans / g << '/' << mm / g << '\n';}std::cout << oss.str();return 0;}
考虑求每个限段被经过的次数的期望值。
注意无论第一次选了种可能方案中的哪一种,都一定会消耗掉相邻的一个球和洞,并规约为一个球洞的问题。所以可以归纳。
设(用越界下标指代不存在的点)表示一开始有个球时,从左往右第个(从右往左第个)球那边那个线段被经过次数的期望值。于是显然有如下递推式:
#include <bits/stdc++.h>using ul = std::uint32_t;using li = std::int32_t;using ull = std::uint64_t;using ll = std::int64_t;ul T;const ul mod = 998244353;ul plus(ul a, ul b){return a + b < mod ? a + b : a + b - mod;}ul mul(ul a, ul b){return ull(a) * ull(b) % mod;}void exgcd(li a, li b, li& x, li&y){if (b) {exgcd(b, a % b, y, x);y -= a / b * x;} else {x = 1;y = 0;}}ul inverse(ul a){li x, y;exgcd(a, mod, x, y);return x < 0 ? x + li(mod) : x;}const ul maxn = 3e3;ul n;ul ans[maxn + 1][maxn + 1];std::stringstream iss;std::stringstream oss;std::string instr;ul inv[maxn + maxn + 1];int main(){std::ios::sync_with_stdio(false);std::cin.tie(0);iss.tie(0);oss.tie(0);std::getline(std::cin, instr, char(0));iss.str(instr);for (ul i = 1; i <= maxn + maxn; ++i) {inv[i] = inverse(i);}for (ul x = 1; x <= maxn; ++x) {for (ul y = 1; x + y - 1 <= maxn; ++y) {ans[x][y] = mul(plus(plus(mul(x + x - 1, ans[x - 1][y]), mul(y + y - 1, ans[x][y - 1])), 1), inv[x + x + y + y - 2]);}}iss >> T;for (ul Case = 1; Case <= T; ++Case) {iss >> n;ul out = 0;li x;iss >> x;for (ul i = 1; i <= n; ++i) {li y;iss >> y >> y;out = plus(out, mul(ans[i][n - i + 1], (y - x) % mod));x = y;}oss << out << std::endl;}std::cout << oss.str();return 0;}
关于官方题解:
首先考虑如下三个问题:①从朵花中选择一些,使得它们占据的点集不相交,最大化选择的花的权值和;②给朵花分别赋予一个非负实数“选择程度”,满足对任何结点,拥有该节点的花的“选择程度”的和小于等于,最大化每朵花的选择程度与其权值的积的和;③给个节点分别赋予一个非负实权值,满足对每朵花,其拥有的结点的权值和大于这朵花的权值,最小化节点的权值和。
于是第一个问题的限制条件强于第二个,于是第一个问题的最优解的值小于等于第二个,但是我不知道怎么证明相等(可以找到系数矩阵不全幺模的例子,所以不能用全幺模这么强的判定方法,不过个人发现生效了的约束好像是全幺模的,但我也不知道怎么证)。而第二个问题和第三个问题是在线性规划意义上对偶的,根据强对偶定理,第二个问题的最优解一定等于第三个。
所以直接动态规划,用表示只考虑以为根的子树完全包含的花所能具有的最大答案,那么转移就有两种来历,一种是直接将赋给,另一种是考虑所有最浅点是的花,要查询出距离为的所有子树中的点的值之和,然后加上,得到的值就是选择这朵花的最大价值,要做到这一点,直接点分治即可。
#include <bits/stdc++.h>using ul = std::uint32_t;using li = std::int32_t;using ull = std::uint64_t;using ll = std::int64_t;ul T;const ul maxn = 1e5;const ul maxm = 1e5;ul n;ul m;ul x[maxm + 1], r[maxm + 1], v[maxm + 1];class circle_t {public:ul x = 0;ul r = 0;ul v = 0;circle_t()=default;circle_t(ul a, ul b, ul c): x(a), r(b), v(c) {}};std::vector<circle_t> circles[maxn + 1];std::vector<ul> edge[maxn + 1];std::stringstream iss;std::stringstream oss;std::string instr;ul rk[maxn + 1];class belong_t {public:ul center = 0;ul type = 0;ul dis = 0;belong_t()=default;belong_t(ul a, ul b, ul c): center(a), type(b), dis(c) {}};std::vector<belong_t> belong[maxn + 1];std::vector<std::vector<ull>> val[maxn + 1];std::vector<ull> valsum[maxn + 1];ull val0[maxn + 1];ul getsz(ul curr, ul prev){ul ret = 1;for (ul next : edge[curr]) {if (next == prev) {continue;}if (rk[next]) {continue;}ret += getsz(next, curr);}return ret;}void search1(ul curr, ul prev, ul fullsz, ul& currsz, ul& mnmxsz, ul& mnmxszid){currsz = 1;ul mxsz = 0;for (ul next : edge[curr]) {if (next == prev) {continue;}if (rk[next]) {continue;}ul nextsz;search1(next, curr, fullsz, nextsz, mnmxsz, mnmxszid);currsz += nextsz;mxsz = std::max(mxsz, nextsz);}mxsz = std::max(mxsz, fullsz - currsz);if (mnmxsz > mxsz) {mnmxszid = curr;mnmxsz = mxsz;}}ul search2(ul curr, ul prev, ul center, ul type, ul dis){ul ret = 0;belong[curr].push_back(belong_t(center, type, dis));for (ul next : edge[curr]) {if (next == prev) {continue;}if (rk[next]) {continue;}ret = std::max(search2(next, curr, center, type, dis + 1), ret);}return ret + 1;}ull querysum(ul x, ul dis){ull ret = 0;for (const auto& b : belong[x]) {if (b.dis == dis) {ret += val0[b.center];} else if (b.dis < dis) {if (valsum[b.center].size() > dis - b.dis) {ret += valsum[b.center][dis - b.dis];if (val[b.center][b.type].size() > dis - b.dis) {ret -= val[b.center][b.type][dis - b.dis];}}}}if (valsum[x].size() > dis) {ret += valsum[x][dis];}return ret;}void change(ul x, ull v){for (const auto& b : belong[x]) {valsum[b.center][b.dis] += v;val[b.center][b.type][b.dis] += v;}val0[x] += v;}ul depth[maxn + 1];ul fa[maxn + 1][17];void search3(ul curr, ul prev){for (ul i = 1; (1 << i) <= depth[curr]; ++i) {fa[curr][i] = fa[fa[curr][i - 1]][i - 1];}for (ul next : edge[curr]) {if (next == prev) {continue;}depth[next] = depth[curr] + 1;fa[next][0] = curr;search3(next, curr);}}void search4(ul curr, ul prev){ull ans = 0;for (ul next : edge[curr]) {if (next == prev) {continue;}search4(next, curr);ans += val0[next];}for (const auto& c : circles[curr]) {ans = std::max(ans, c.v + querysum(c.x, c.r + 1));}change(curr, ans);}int main(){std::ios::sync_with_stdio(false);std::cin.tie(0);iss.tie(0);oss.tie(0);std::getline(std::cin, instr, char(0));iss.str(instr);iss >> T;for (ul Case = 1; Case <= T; ++Case) {iss >> n >> m;for (ul i = 1; i <= n; ++i) {edge[i].resize(0);rk[i] = 0;belong[i].resize(0);val[i].resize(0);valsum[i].resize(0);val0[i] = 0;circles[i].resize(0);}for (ul i = 1; i <= n - 1; ++i) {ul u, v;iss >> u >> v;edge[u].push_back(v);edge[v].push_back(u);}for (ul i = 1; i <= m; ++i) {iss >> x[i] >> r[i] >> v[i];}for (ul i = 1; i <= n; ++i) {while (!rk[i]) {ul sz = getsz(i, 0);ul mnmxsz = sz;ul mnmxszid = i;ul tmp;search1(i, 0, sz, tmp, mnmxsz, mnmxszid);rk[mnmxszid] = sz;ul maxmaxlen = 0;for (ul next : edge[mnmxszid]) {if (rk[next]) {continue;}ul maxlen = search2(next, 0, mnmxszid, val[mnmxszid].size(), 1);val[mnmxszid].push_back(std::vector<ull>(maxlen + 1, 0));maxmaxlen = std::max(maxlen, maxmaxlen);}valsum[mnmxszid].resize(maxmaxlen + 1, 0);}}depth[1] = 0;search3(1, 0);for (ul i = 1; i <= m; ++i) {if (r[i] > depth[x[i]]) {circles[1].push_back(circle_t(x[i], r[i], v[i]));} else {ul tmpx = x[i];ul tmpr = r[i];while (tmpr) {ul tmp = __builtin_ctz(tmpr);tmpx = fa[tmpx][tmp];tmpr -= 1 << tmp;}circles[tmpx].push_back(circle_t(x[i], r[i], v[i]));}}search4(1, 0);oss << val0[1] << '\n';}std::cout << oss.str();return 0;}
官方题解如下:

直接使用方法二做,证明很容易,只要注意到必胜策略同样适用于“无视重复”的情况。
#include <bits/stdc++.h>using ul = std::uint32_t;using li = std::int32_t;using ull = std::uint64_t;using ll = std::int64_t;using us = std::uint16_t;ul T;const ul maxn = 2000;ul n;li x[maxn + 1], y[maxn + 1];ll dis[maxn + 1][maxn + 1];std::vector<std::pair<us, us>> edges(maxn * maxn);bool ans[maxn + 1][maxn + 1];bool suf[maxn + 1];ll sqr(ll a){return a * a;}std::stringstream iss;std::stringstream oss;std::string instr;int main(){std::ios::sync_with_stdio(false);std::cin.tie(0);iss.tie(0);oss.tie(0);std::freopen("G.in", "r", stdin);std::getline(std::cin, instr, char(0));iss.str(instr);iss >> T;for (ul Case = 1; Case <= T; ++Case) {iss >> n;edges.resize(0);for (ul i = 1; i <= n; ++i) {iss >> x[i] >> y[i];suf[i] = true;for (ul j = 1; j != i; ++j) {dis[i][j] = sqr(x[i] - x[j]) + sqr(y[i] - y[j]);edges.push_back(std::pair<us, us>(i, j));}}std::sort(edges.begin(), edges.end(), [&](const std::pair<us, us>& a, const std::pair<us, us>& b){return dis[a.first][a.second] > dis[b.first][b.second];});for (ul l = 0, r = 0; l != edges.size(); l = r) {for ( ; r != edges.size() && dis[edges[l].first][edges[l].second] == dis[edges[r].first][edges[r].second]; ++r);for (ul i = l; i != r; ++i) {ans[edges[i].first][edges[i].second] = !suf[edges[i].second];ans[edges[i].second][edges[i].first] = !suf[edges[i].first];}for (ul i = l; i != r; ++i) {suf[edges[i].first] = suf[edges[i].first] && ans[edges[i].first][edges[i].second];suf[edges[i].second] = suf[edges[i].second] && ans[edges[i].second][edges[i].first];}}oss << (suf[1] ? "NO\n" : "YES\n");}std::freopen("out.txt", "w", stdout);std::cout << oss.str();return 0;}
关于如何实现计算
现将上述运算简记为。
现在设表示允许使用这些碎片且碎片集合刚好为的所有符卡的伤害之和,那么显然,,其中表示碎片编号最大为,且碎片集合刚好为的所有弹幕的伤害之和。而则等于所有无需碎片的弹幕的伤害加一之积。
#include <bits/stdc++.h>std::stringstream iss;std::stringstream oss;std::string instr;using ul = std::uint32_t;using li = std::int32_t;using ull = std::uint64_t;using ll = std::int64_t;const ul mod = 998244353;ul plus(ul a, ul b){return a + b < mod ? a + b : a + b - mod;}ul minus(ul a, ul b){return a < b ? a + mod - b : a - b;}ul mul(ul a, ul b){return ull(a) * ull(b) % mod;}ul v[1 << 21][22];ul ans[1 << 21][22];std::vector<std::pair<ul, ul>> danmaku[21];int main(){std::ios::sync_with_stdio(false);std::cin.tie(0);iss.tie(0);oss.tie(0);std::getline(std::cin, instr, char(0));iss.str(instr);ans[0][0] = 1;ul n;iss >> n;for (ul i = 1; i <= n; ++i) {ul p, b;iss >> p >> b;if (b == 0) {ans[0][0] = mul(ans[0][0], plus(p, 1));} else {danmaku[31 - __builtin_clz(b)].push_back(std::pair<ul, ul>(p, b));}}for (ul cnt = 1; cnt <= 21; ++cnt) {for (const auto& p : danmaku[cnt - 1]) {v[p.second][__builtin_popcount(p.second)] = plus(v[p.second][__builtin_popcount(p.second)], p.first);}v[0][0] = 1;for (ul i = 0; i != cnt; ++i) {ul set = (1 << cnt) - 1 & ~(1 << i);for (ul sub = set; ; sub = sub - 1 & set) {for (ul t = 0; t <= cnt; ++t) {v[sub | 1 << i][t] = plus(v[sub | 1 << i][t], v[sub][t]);ans[sub | 1 << i][t] = plus(ans[sub | 1 << i][t], ans[sub][t]);}if (!sub) {break;}}}for (ul set = 0; set != (1 << cnt); ++set) {for (ul sz = cnt; ~sz; --sz) {ul tmp = 0;for (ul i = 0; i <= sz; ++i) {tmp = plus(tmp, mul(v[set][i], ans[set][sz - i]));}ans[set][sz] = tmp;}}for (ul i = 0; i != cnt; ++i) {ul set = (1 << cnt) - 1 & ~(1 << i);for (ul sub = set; ; sub = sub - 1 & set) {for (ul t = 0; t <= cnt; ++t) {ans[sub | 1 << i][t] = minus(ans[sub | 1 << i][t], ans[sub][t]);}if (!sub) {break;}}}std::memset(v, 0, sizeof(ul) * 22 << cnt);}ul m;iss >> m;for (ul i = 1; i <= m; ++i) {ul x;iss >> x;oss << ans[x][__builtin_popcount(x)] << '\n';}std::cout << oss.str();return 0;}
官方题解如下:
其中“证明显然”并不显然。首先考虑狄尔沃斯定理,于是知道长为的置换的最长上升子序列的长度正好就是最小下降序列覆盖的大小,也就是说,其可以被拆为个长度不超过最长下降子序列的长度的子序列的并,故。
而这一结果确实是字典序最小的这一点的证明如下:考虑对使用归纳法。(的情况显然)
设。
如果,那么,于是直接可以把问题约归为的问题。
如果,那么由于必须将序列分为个下降子列的并,而每个下降子列长度不超过,这意味着所属的下降子列的长度至少为,于是至少是,于是问题被约归为的问题。
#include <bits/stdc++.h>std::stringstream iss;std::stringstream oss;std::string instr;using ul = std::uint32_t;using li = std::int32_t;using ull = std::uint64_t;using ll = std::int64_t;ul T;ul n, x, y;const ul maxn = 1e5;ul ans[maxn + 1];int main(){std::ios::sync_with_stdio(false);std::cin.tie(0);iss.tie(0);oss.tie(0);std::getline(std::cin, instr, char(0));iss.str(instr);iss >> T;for (ul Case = 1; Case <= T; ++Case) {iss >> n >> x >> y;if (ull(x) * y < n || x + y - 1 > n) {oss << "NO\n";continue;}oss << "YES\n";ul a = x, b = y;for (ul i = 1; i <= n; ++i) {ans[i] = i;}if (b != 1) {ul q = (n - a + b - 2) / (b - 1);std::reverse(ans + a - q + 1, ans + n - (q - 1) * b + 1);for (ul i = 1; i <= q - 1; ++i) {std::reverse(ans + n - i * b + 1, ans + n - (i - 1) * b + 1);}}for (ul i = 1; i <= n; ++i) {if (i != 1) {oss << ' ';}oss << ans[i];}oss << '\n';}std::cout << oss.str();return 0;}
官方题解如下:
其实关于时间复杂度的证明稍微有点问题。因为中不止需要是质数,而且不能落在中,当然到底怎么证明不清楚。
#include <bits/stdc++.h>std::stringstream iss;std::stringstream oss;std::string instr;using ul = std::uint32_t;using li = std::int32_t;using ull = std::uint64_t;using ll = std::int64_t;ul T;ll gcd(ll a, ll b){while (b) {b ^= a ^= b ^= a %= b;}return a;}class vec {public:ll x = 0;ll y = 0;vec()=default;vec(ll a, ll b): x(a), y(b) {}};bool operator<(const vec& a, const vec& b){return a.x != b.x ? a.x < b.x : a.y < b.y;}vec operator+(const vec& a, const vec& b){return vec(a.x + b.x, a.y + b.y);}const std::vector<vec> dirs = {vec(-1, -1), vec(-1, 0), vec(-1, 1), vec(0, -1), vec(0, 0), vec(0, 1), vec(1, -1), vec(1, 0), vec(1, 1)};vec s;std::deque<vec> queue;std::set<vec> set;int main(){std::ios::sync_with_stdio(false);std::cin.tie(0);iss.tie(0);oss.tie(0);std::getline(std::cin, instr, char(0));iss.str(instr);iss >> T;for (ul Case = 1; Case <= T; ++Case) {iss >> s.x >> s.y;if (s.x == s.y) {oss << "0/1\n";continue;}while (queue.size()) {queue.pop_front();}set.clear();queue.push_back(s);set.insert(s);ul cnt1 = 0, cnt2 = 0;for (const auto& dir : dirs) {auto next = s + dir;if (gcd(next.x, next.y) > 1) {++cnt1;}}bool flag = false;while (queue.size()) {auto curr = queue.front();queue.pop_front();for (const auto& dir : dirs) {auto next = curr + dir;if (next.x == next.y) {oss << "0/1\n";flag = true;break;}ll g = gcd(next.x, next.y);if (g > 1) {++cnt2;}if (g > 1 && set.insert(next).second) {queue.push_back(next);}}if (flag) {break;}}if (!flag) {ul g = gcd(cnt1, cnt2);oss << cnt1 / g << '/' << cnt2 / g << '\n';}}std::cout << oss.str();return 0;}
官方题解如下:

#include <bits/stdc++.h>std::stringstream iss;std::stringstream oss;std::string instr;using ul = std::uint32_t;using li = std::int32_t;using ull = std::uint64_t;using ll = std::int64_t;const ul mod = 1e9 + 7;ul plus(ul a, ul b){return a + b < mod ? a + b : a + b - mod;}ul minus(ul a, ul b){return a < b ? a + mod - b : a - b;}ul mul(ul a, ul b){return ull(a) * ull(b) % mod;}void exgcd(li a, li b, li& x, li& y){if (b) {exgcd(b, a % b, y, x);y -= a / b * x;} else {x = 1;y = 0;}}ul inverse(ul a){li x, y;exgcd(a, mod, x, y);return x < 0 ? x + li(mod) : x;}ul T;ul n;const ul maxn = 100;li a[maxn + 1];ul fac[maxn + 1];ul fiv[maxn + 1];ul ans[maxn + 2][maxn + 2][maxn + 1];ul al[maxn + 2][maxn + 2][maxn + 1];ul tim = 0;ul sbi(ul a, ul b){return mul(fac[a + b], mul(fiv[a], fiv[b]));}void search(ul dep, ul l, ul r){if (tim == al[dep][l][r]) {return;}al[dep][l][r] = tim;if (r < l) {ans[dep][l][r] = 1;return;}ans[dep][l][r] = 0;for (ul i = l; i <= r; ++i) {if (~a[i] && a[i] != dep) {continue;}if (i + i < l + r) {search(dep, l, i - 1);if (!ans[dep][l][i - 1]) {continue;}search(dep + 1, i + 1, r);if (!ans[dep + 1][i + 1][r]) {continue;}} else {search(dep + 1, i + 1, r);if (!ans[dep + 1][i + 1][r]) {continue;}search(dep, l, i - 1);if (!ans[dep][l][i - 1]) {continue;}}ans[dep][l][r] = plus(ans[dep][l][r], mul(mul(ans[dep][l][i - 1], ans[dep + 1][i + 1][r]), sbi(i - l, r - i)));}}int main(){std::ios::sync_with_stdio(false);std::cin.tie(0);iss.tie(0);oss.tie(0);std::getline(std::cin, instr, char(0));iss.str(instr);fac[0] = 1;for (ul i = 1; i <= maxn; ++i) {fac[i] = mul(fac[i - 1], i);}fiv[maxn] = inverse(fac[maxn]);for (ul i = maxn; i >= 1; --i) {fiv[i - 1] = mul(fiv[i], i);}iss >> T;for (ul Case = 1; Case <= T; ++Case) {iss >> n;for (ul i = 1; i <= n; ++i) {iss >> a[i];}++tim;search(1, 1, n);oss << ans[1][1][n] << '\n';}std::cout << oss.str();return 0;}