@qq290637843
2020-09-28T05:02:19.000000Z
字数 31730
阅读 734
该题需要用到有限Thue-Morse串的紧致后缀自动机。关于其结构的证明见https://chaoli.club/index.php/5638,当然,此论文的证明有一些纰漏,暂时没能解决。
虽然那篇论文里犯了错误,不等于该题解就要继承那个错误。在该题解中,左右特殊子串定义如下:
(1)如果一个串满足以下两个条件的析取,则称是的左特殊子串:
①是的前缀;
②存在不同的字符和满足和都是的子串;
(2)如果一个串满足以下两个条件的析取,则称是的右特殊子串:
①是的后缀;
②存在不同的字符和满足和都是的子串;
如果一个串既是的左特殊子串,又是的右特殊子串,则称为的双特殊子串。
于是可知,的紧致后缀自动机中所保留的点对应的就是的双特殊子串。
对于输入,只要将作为输入在的紧致后缀自动机上运行,最后到达的节点,实际上就是满足“是的子串且是的双特殊子串”的最短的。并且还要注意到,如果紧致后缀自动机中经过(是字符,是字符串)到达,那么是满足“是的子串,且是双特殊子串”中最短的。
所以题目做法就是,将输入作为的禁止后缀自动机的输入,得到一个节点。接着,该节点所能表示的那些子串的出现次数就是从其出发到紧致后缀自动机的接受态的路线数量(每一条路线都是一个后缀),于是就形成了一个从右向左的线性递推式,可以用伯莱坎普梅西算法直接获取递推式而不必自己在那弄矩阵。关于伯莱坎普梅西算法,见https://chaoli.club/index.php/5641。
现在既然已经知道怎么求一个节点所对应字符串在中的出现次数,那么还有第二个问题要处理。也就是说要解决“对于节点,有多少其子串不包含于的更短的双特殊子串”,于是这时就要借助于双特殊子串的性质(自己思考),以及论文中的一些结论(假定它们是对的,因为那个纰漏我实在没空处理了),来确定的严格子串中那些极长的的双特殊子串。统一方法是:已知是的双特殊字串,如果是极长的满足“既是的严格子串,又是的双特殊子串”的串,那么一定要不在紧致后缀自动机上走一条边就到达,要不就是的后缀。于是乎,就可以解决上面的问题了。于是,这些极长串就划定了一些区间,它们互相之间不包含而且能覆盖那个长的双特殊子串(这一点请读者自己思考)。于是问题就成了:在一大区间上,有一些小区间,它们之间无包含关系,求大区间有多少子区间不包含于任何一个小区间。此问题请参考华林公式https://chaoli.club/index.php/5490解决。
综上,问题就解决了,时间复杂度是(用题干里的记号)。
#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 uss = std::uint8_t;using llf = long double;const ul maxlen = 1e6;ul T;ull n;char s[maxlen + 1];std::map<std::string, ul> cnt[4];ul cntcnt[4][1 << 3];enum str_class {tau,sigma,barsigma,bartau,eps,nst};class state {public:str_class c = eps;ul i = 0;state()=default;state(str_class a, ul b): c(a), i(b) {}};bool check(ul& pos, str_class c, ul stri){ul len = 1 << stri;for (ul i = 0; s[pos] && i != len; ++pos, ++i) {if (s[pos] != ((__builtin_popcount(i) ^ (c != tau)) & 1) + 'a') {return false;}}return true;}void onestep(ul& pos, state& curr){if (curr.c == eps) {if (s[pos] == 'a') {if (check(pos, tau, 0)) {curr.c = tau;curr.i = 0;} else {curr.c = nst;}} else if (s[pos] == 'b') {if (check(pos, bartau, 0)) {curr.c = bartau;curr.i = 0;} else {curr.c = nst;}}} else if (curr.c == tau) {if (curr.i == 0) {if (s[pos] == 'a') {if (check(pos, tau, 1)) {curr.c = bartau;curr.i = 2;} else {curr.c = nst;}} else if (s[pos] == 'b') {if (check(pos, bartau, 0)) {curr.c = tau;curr.i = 1;} else {curr.c = nst;}}} else if (curr.i == n - 2) {if (s[pos] == 'a') {if (check(pos, tau, n - 3) && check(pos, tau, n - 2)) {curr.c = tau;curr.i = n;} else {curr.c = nst;}} else if (s[pos] == 'b') {if (check(pos, bartau, n - 2) && check(pos, bartau, n - 1)) {curr.c = tau;curr.i = n;} else {curr.c = nst;}}} else {if (s[pos] == 'a') {if (check(pos, tau, curr.i - 1)) {curr.c = sigma;++curr.i;} else {curr.c = nst;}} else if (s[pos] == 'b') {if (check(pos, bartau, curr.i)) {curr.c = tau;++curr.i;} else {curr.c = nst;}}}} else if (curr.c == sigma) {if (curr.i == n - 2) {if (s[pos] == 'a') {if (check(pos, tau, n - 3) && check(pos, bartau, n - 1)) {curr.c = tau;curr.i = n;} else {curr.c = nst;}} else if (s[pos] == 'b') {if (check(pos, bartau, n - 4) && check(pos, bartau, n - 3)) {curr.c = tau;curr.i = n;} else {curr.c = nst;}}} else {if (s[pos] == 'a') {if (check(pos, tau, curr.i - 1)) {curr.c = tau;++curr.i;} else {curr.c = nst;}} else if (s[pos] == 'b') {if (check(pos, bartau, curr.i - 2) && check(pos, bartau, curr.i - 1)) {curr.c = bartau;++curr.i;} else {curr.c = nst;}}}} else if (curr.c == barsigma) {if (curr.i == n - 2) {if (s[pos] == 'a') {if (check(pos, tau, n - 4) && check(pos, tau, n - 3) && check(pos, bartau, n - 1)) {curr.c = tau;curr.i = n;} else {curr.c = nst;}} else if (s[pos] == 'b') {if (check(pos, bartau, n - 3)) {curr.c = tau;curr.i = n;} else {curr.c = nst;}}} else {if (s[pos] == 'a') {if (check(pos, tau, curr.i - 2) && check(pos, tau, curr.i - 1)) {curr.c = tau;++curr.i;} else {curr.c = nst;}} else if (s[pos] == 'b') {if (check(pos, bartau, curr.i - 1)) {curr.c = bartau;++curr.i;} else {curr.c = nst;}}}} else if (curr.c == bartau) {if (curr.i == 0) {if (s[pos] == 'a') {if (check(pos, tau, 0)) {curr.c = bartau;curr.i = 1;} else {curr.c = nst;}} else if (s[pos] == 'b') {if (check(pos, bartau, 1)) {curr.c = tau;curr.i = 2;} else {curr.c = nst;}}} else if (curr.i == n - 2) {if (s[pos] == 'a') {if (check(pos, tau, n - 2)) {curr.c = tau;curr.i = n;} else {curr.c = nst;}} else if (s[pos] == 'b') {if (check(pos, bartau, n - 1)) {curr.c = tau;curr.i = n;} else {curr.c = nst;}}} else {if (s[pos] == 'a') {if (check(pos, tau, curr.i)) {curr.c = bartau;++curr.i;} else {curr.c = nst;}} else if (s[pos] == 'b') {if (check(pos, bartau, curr.i - 1)) {curr.c = barsigma;++curr.i;} else {curr.c = nst;}}}}}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 ? li(mod) + x : x;}ul ppow(ul a, ull b){ul ret = 1;while (b) {if (b & 1) {ret = mul(ret, a);}a = mul(a, a);b >>= 1;}return ret;}const ul maxllen = 64;ul ans[10][maxllen];ul cc[10][maxllen + 1];ul len[10];ul bm(const ul s[], ul cc[], ul nn){static ul bb[maxllen + 1];static ul tt[maxllen + 1];cc[0] = 1;bb[0] = 1;ul tl;ul bl = 0;ul l = 0;ul m = 1;ul b = 1;for (ul n = 0; n != nn; ++n) {ul d = 0;for (ul i = 0; i <= l; ++i) {d = plus(d, mul(cc[i], s[n - i]));}if (d == 0) {++m;} else if (l + l <= n) {std::memcpy(tt, cc, sizeof(ul) * (l + 1));tl = l;std::memset(&cc[l + 1], 0, sizeof(ul) * (n - l - l + 1));l = n + 1 - l;ul lambda = mul(inverse(b), d);for (ul i = 0; i <= bl; ++i) {cc[i + m] = minus(cc[i + m], mul(lambda, bb[i]));}bl = tl;std::memcpy(bb, tt, sizeof(ul) * (bl + 1));b = d;m = 1;} else {ul lambda = mul(inverse(b), d);for (ul i = 0; i <= bl; ++i) {cc[i + m] = minus(cc[i + m], mul(lambda, bb[i]));}++m;}}return l;}ul linearrec(const ul s[], const ul tcc[], ul len, ull n){static ul cc[maxllen + 1];static ul ret[maxllen + 1];static ul temp[2943];if (n < len) {return s[n];}std::memcpy(cc, tcc, sizeof(ul) * (len + 1));std::memset(ret, 0, sizeof(ul) * len);ret[0] = 1;ul dret = 0;for (ull t = ull(1) << 63 - __builtin_clzll(n); t; t >>= 1) {std::memset(temp, 0, sizeof(ul) * (dret + dret + 1));for (ul i = 0; i <= dret; ++i) {temp[i + i] = plus(temp[i + i], mul(ret[i], ret[i]));for (ul j = i + 1; j <= dret; ++j) {ul tp = mul(ret[i], ret[j]);temp[i + j] = plus(temp[i + j], plus(tp, tp));}}for (ul i = dret + dret; i >= len; --i) {ul lambda = minus(0, temp[i]);if (!lambda) {continue;}for (ul j = 0; j <= len; ++j) {temp[i - j] = plus(temp[i - j], mul(cc[j], lambda));}}std::memcpy(ret, temp, sizeof(ul) * len);dret = std::min(dret + dret, len - ul(1));if (t & n) {for (ul i = dret + 1; i; --i) {ret[i] = ret[i - 1];}ret[0] = 0;++dret;if (dret == len) {ul lambda = minus(0, ret[len]);if (lambda) {for (ul i = 0; i <= len; ++i) {ret[len - i] = plus(ret[len - i], mul(cc[i], lambda));}}--dret;}}}ul out = 0;for (ul i = 0; i != len; ++i) {out = plus(out, mul(s[i], ret[i]));}return out;}ul div2(ul x){return (x & 1) ? (x + mod >> 1) : (x >> 1);}ul g(ul x){return div2(mul(x, plus(x, 1)));}ul ftaun(ull n){return minus(plus(g(ppow(2, n)), mul(8, g(ppow(2, n - 3)))), plus(mul(5, g(ppow(2, n - 2))), mul(4, g(mul(3, ppow(2, n - 4))))));}ul ftau(ull n){if (n == 1) {return 1;} else if (n == 2) {return 4;} else {return plus(minus(g(ppow(2, n)), mul(2, plus(g(ppow(2, n - 1)), g(mul(3, ppow(2, n - 3)))))), mul(3, g(ppow(2, n - 2))));}}ul fsigma(ull n){return minus(plus(g(mul(3, ppow(2, n - 2))), g(ppow(2, n - 2))), mul(2, g(ppow(2, n - 1))));}int main(){ans[tau][0] = 1;ans[tau][2] = 3;ans[sigma][2] = 2;ans[barsigma][2] = 2;ans[bartau][2] = 2;for (ul i = 3; i != maxllen; ++i) {ans[tau][i] = plus(plus(ans[tau][i - 1], ans[sigma][i - 1]), ~i & 1);ans[sigma][i] = plus(ans[tau][i - 1], ans[bartau][i - 1]);ans[barsigma][i] = plus(ans[tau][i - 1], ans[bartau][i - 1]);ans[bartau][i] = plus(plus(ans[barsigma][i - 1], ans[bartau][i - 1]), i & 1);}for (auto t : {tau, sigma, barsigma, bartau}) {len[t] = bm(ans[t], cc[t], maxllen);}for (ul n = 0; n <= 3; ++n) {for (ul i = 0; i != (1 << n); ++i) {s[i] = (__builtin_popcount(i) & 1) + 'a';s[i + 1] = 0;for (ul j = 0; j <= i; ++j) {++cnt[n][std::string(s + j)];}}for (const auto& p : cnt[n]) {++cntcnt[n][p.second];}}std::scanf("%u", &T);for (ul Case = 1; Case <= T; ++Case) {std::scanf("%llu%s", &n, s);if (n <= 3) {auto it = cnt[n].find(std::string(s));if (it != cnt[n].end()) {std::printf("%u %u\n", it->second, cntcnt[n][it->second]);} else {std::printf("-1\n");}continue;}state st;ul pos = 0;while (s[pos] && st.c != nst) {onestep(pos, st);}if (st.c == nst) {std::printf("-1\n");continue;}ul out;if (st.i != 0) {out = linearrec(ans[st.c], cc[st.c], len[st.c], n - st.i);} else if (st.c == tau) {out = plus(linearrec(ans[tau], cc[tau], len[tau], n - 1), linearrec(ans[bartau], cc[bartau], len[bartau], n - 2));out = plus(out, ~n & 1);} else if (st.c == bartau) {out = plus(linearrec(ans[bartau], cc[bartau], len[bartau], n - 1), linearrec(ans[tau], cc[tau], len[tau], n - 2));out = plus(out, n & 1);}ul out2;if (st.i == n) {out2 = ftaun(n);} else if (st.i == 0) {out2 = 2;} else {out2 = 0;for (auto t : {tau, bartau}) {if (linearrec(ans[t], cc[t], len[t], n - st.i) == out) {out2 = plus(out2, ftau(st.i));}}if (st.i != 1) {for (auto t : {sigma, barsigma}) {if (linearrec(ans[t], cc[t], len[t], n - st.i) == out) {out2 = plus(out2, fsigma(st.i));}}}}std::printf("%u %u\n", out, out2);}return 0;}
给定一个无向图,要求拆成尽量少的森林的并。
首先想到这是一个拟阵划分问题,关于拟阵划分算法正确性的证明见https://chaoli.club/index.php/5645。论文中的叙述实际上等价于如下描述:
考虑现在已经有个独立集,新添加一个元素,能继续把加进已经有的独立集当且仅当能找到这样一条路线:(有可能是,不要求必须不同,但是必须不同),这条路径满足下列条件:必须在的圈中,必须独立。如果有这么条路线,就可以增广,如果没有说明必须新建独立集才能容下了。于是就可以广搜找增广路。
这样做的话每次增广访问独立集的次数能达到,再算上次增广,故总计,所以还要考虑新东西。论文中是针对更宽泛的情况,就是说,个拟阵之间不要求相同,但在相同的时候,每一次增广访问独立集的次数其实可以只到,考虑这样一件事:在同拟阵的背景下,如果存在一条增广路,则一定存在形如下方这样的增广路(用表示)。为什么呢,假设有一个别的形状的增广路,能强行把增广路改成这样的形式只要能证明如下命题即可:如果,是独立集,不独立且独立,则在的圈中至少有一条边满足独立。该命题的证明直接重复使用赖虹健《拟阵论》的(1.2.4)即可。于是现在只要能做到每次增广,每条边都只访问一次,那么时间可以在,表示单次对边访问的时间。于是我直接用重链剖分套treap用的时间把这道题糊过去了,但是实际上这道题存在的算法,由塔尖发明,我心情好了再说吧(连做两道有点硬的题,有点想休息一下了)。
#include <bits/stdc++.h>using ul = std::uint32_t;using li = std::int32_t;using ull = std::uint64_t;using ll = std::int64_t;std::mt19937 rnd;class treap {public:class node {public:ul key = 0;ul value = 0;ul weight = 0;ul lson = 0;ul rson = 0;ul fa = 0;node()=default;node(ul a, ul b): key(a), value(b), weight(rnd()) {}};std::vector<node> tree;void clear() {tree.resize(1);tree[0].lson = 0;}void rotate(ul x) {ul y = tree[x].fa;if (tree[y].lson == x) {tree[y].lson = tree[x].rson;tree[x].rson = y;tree[tree[y].lson].fa = y;} else {tree[y].rson = tree[x].lson;tree[x].lson = y;tree[tree[y].rson].fa = y;}tree[x].fa = tree[y].fa;tree[y].fa = x;if (tree[tree[x].fa].lson == y) {tree[tree[x].fa].lson = x;} else {tree[tree[x].fa].rson = x;}}void insert(ul a, ul b) {if (!tree[0].lson) {tree[0].lson = tree.size();tree.push_back(node(a, b));return;}ul c = tree[0].lson;for ( ; ; ) {if (a < tree[c].key) {if (tree[c].lson) {c = tree[c].lson;} else {tree[c].lson = tree.size();tree.push_back(node(a, b));tree.back().fa = c;break;}} else if (a > tree[c].key) {if (tree[c].rson) {c = tree[c].rson;} else {tree[c].rson = tree.size();tree.push_back(node(a, b));tree.back().fa = c;break;}} else {tree[c].value = b;return;}}c = tree.size() - 1;while (tree[c].fa && tree[tree[c].fa].weight < tree[c].weight) {rotate(c);}}ul lower_bound(ul a) {ul c = tree[0].lson;if (!c) {return 0;}ul ret = 0;for ( ; ; ) {if (a > tree[c].key) {if (tree[c].rson) {c = tree[c].rson;} else {return ret;}} else {ret = c;if (tree[c].lson) {c = tree[c].lson;} else {return ret;}}}}ul find(ul a) {ul c = tree[0].lson;if (!c) {return 0;}for ( ; ; ) {if (a > tree[c].key) {if (tree[c].rson) {c = tree[c].rson;} else {return 0;}} else if (a < tree[c].key) {if (tree[c].lson) {c = tree[c].lson;} else {return 0;}} else {return c;}}}ul next(ul c) {if (tree[c].rson) {c = tree[c].rson;for ( ; ; ) {if (tree[c].lson) {c = tree[c].lson;} else {return c;}}} else {ul k = tree[c].key;for (c = tree[c].fa; ; c = tree[c].fa) {if (!c || tree[c].key > k) {return c;}}}}ul erase(ul c) {ul ret = next(c);for ( ; ; ) {if (!tree[c].lson && !tree[c].rson) {if (tree[tree[c].fa].lson == c) {tree[tree[c].fa].lson = 0;} else {tree[tree[c].fa].rson = 0;}return ret;} else if (!tree[c].lson) {rotate(tree[c].rson);} else if (!tree[c].rson) {rotate(tree[c].lson);} else {if (tree[tree[c].lson].weight > tree[tree[c].rson].weight) {rotate(tree[c].lson);} else {rotate(tree[c].rson);}}}}};ul T;const ul maxn = 1000;const ul maxm = 2000;ul n, m;class edge_t {public:ul u = 0;ul v = 0;ul nextforu = 0;ul nextforv = 0;ul& next(ul t) {return t == u ? nextforu : nextforv;}ul next(ul t) const {return t == u ? nextforu : nextforv;}ul to(ul t) const {return t ^ u ^ v;}};edge_t edge[maxm + 1];ul k;ul head[maxm + 1][maxn + 1];treap forest[maxm + 1];std::vector<ul> node[maxm + 1];ul ex[maxm + 1][maxn + 1];ul alfornode[maxm + 1][maxn + 1];ul tim = 0;ul timex[maxm + 1];ul depth[maxm + 1][maxn + 1];ul bigson[maxm + 1][maxn + 1];ul fa[maxm + 1][maxn + 1];ul hvhd[maxm + 1][maxn + 1];ul rootin[maxm + 1][maxn + 1];treap road[maxm + 1][maxn + 1];void addedge(ul from, ul eid, ul& head){edge[eid].next(from) = head;head = eid;}void search(ul curr, ul prev, ul depth[], const ul head[], ul& sz, ul bigson[], ul fa[], ul alfornode[], ul rootin[]){alfornode[curr] = tim;depth[curr] = depth[prev] + 1;sz = 1;bigson[curr] = 0;ul bignsz = 0;for (ul eid = head[curr]; eid; eid = edge[eid].next(curr)) {ul next = edge[eid].to(curr);if (next == prev) {continue;}rootin[next] = rootin[curr];fa[next] = curr;ul nsz;search(next, curr, depth, head, nsz, bigson, fa, alfornode, rootin);sz += nsz;if (nsz > bignsz) {bigson[curr] = next;bignsz = nsz;}}}void search2(ul curr, ul prev, const ul depth[], const ul head[], const ul bigson[], ul hvhd[], treap road[]){for (ul eid = head[curr]; eid; eid = edge[eid].next(curr)) {ul next = edge[eid].to(curr);if (next == prev) {continue;}if (next == bigson[curr]) {hvhd[next] = hvhd[curr];} else {hvhd[next] = next;road[next].clear();}road[hvhd[next]].insert(depth[next], eid);search2(next, curr, depth, head, bigson, hvhd, road);}}std::deque<ul> queue;ul prev[maxm + 1];ul belong[maxm + 1];void lca(ul u, ul v, const ul depth[], const ul hvhd[], const ul fa[], treap road[], ul curr){while (hvhd[u] != hvhd[v]) {if (depth[hvhd[u]] < depth[hvhd[v]]) {std::swap(u, v);}ul l = depth[hvhd[u]];ul r = depth[u];treap& ro = road[hvhd[u]];u = fa[hvhd[u]];for (auto it = ro.lower_bound(l); it && ro.tree[it].key <= r; it = ro.erase(it)) {queue.push_back(ro.tree[it].value);prev[ro.tree[it].value] = curr;}}if (depth[u] > depth[v]) {std::swap(u, v);}ul l = depth[u] + 1;ul r = depth[v];treap& ro = road[hvhd[u]];for (auto it = ro.lower_bound(l); it && ro.tree[it].key <= r; it = ro.erase(it)) {queue.push_back(ro.tree[it].value);prev[ro.tree[it].value] = curr;}}int main(){rnd.seed(std::time(0));std::scanf("%u", &T);for (ul Case = 1; Case <= T; ++Case) {std::scanf("%u%u", &n, &m);k = 0;for (ul t = 1; t <= m; ++t) {std::scanf("%u%u", &edge[t].u, &edge[t].v);belong[t] = 0;for (ul i = 1; i <= k; ++i) {node[i].resize(0);++timex[i];for (ul it = forest[i].lower_bound(0); it; it = forest[i].next(it)) {ul eid = forest[i].tree[it].key;belong[eid] = i;ul u = edge[eid].u;ul v = edge[eid].v;if (ex[i][u] != timex[i]) {node[i].push_back(u);ex[i][u] = timex[i];head[i][u] = 0;}if (ex[i][v] != timex[i]) {node[i].push_back(v);ex[i][v] = timex[i];head[i][v] = 0;}addedge(u, eid, head[i][u]);addedge(v, eid, head[i][v]);}++tim;for (ul root : node[i]) {if (alfornode[i][root] == tim) {continue;}ul sz = 0;rootin[i][root] = root;search(root, 0, depth[i], head[i], sz, bigson[i], fa[i], alfornode[i], rootin[i]);hvhd[i][root] = root;road[i][root].clear();search2(root, 0, depth[i], head[i], bigson[i], hvhd[i], road[i]);}}while (queue.size()) {queue.pop_front();}queue.push_back(t);ul end = 0;ul endprev = 0;if (k) {while (queue.size() && !end) {ul curr = queue.front();queue.pop_front();ul u = edge[curr].u;ul v = edge[curr].v;ul i = belong[curr] % k + 1;if (ex[i][u] != timex[i] || ex[i][v] != timex[i] || rootin[i][u] != rootin[i][v]) {end = i;endprev = curr;} else {lca(u, v, depth[i], hvhd[i], fa[i], road[i], curr);}}}if (!end) {++k;forest[k].clear();forest[k].insert(t, 0);} else {for (ul a = end, b = endprev; ; ) {forest[a].insert(b, 0);if (belong[b]) {forest[belong[b]].erase(forest[belong[b]].find(b));a = belong[b];b = prev[b];} else {break;}}}}std::printf("%u\n", k);}return 0;}
直接按照官方题解做就是,如下:
如果,直接构造为:X.X.X.X.的形式,长为
如果,设,于是图如下:
XXXXXXXXXXXXXXXXXXXXXXXXXX.X.X.X.X.X.X.X.X.X.X.X.XXXXXXXXXXXXXXXXXXXXXXXXXXX.X.X.X.X.X.X.X.X.X.X.X.XXXXXXXXXXXXXXXXXXXXXXXXXXX.X.X.X.X.X.X.X.X.X.X.X.XXXXXXXXXXXXXXXXXXXXXXXXXXX.X.X.X.X.X.X.X.X.X.X.X.XXXXXXXXXXXXXXXXXXXXXXXXXXX.X.X.X.X.X.X.X.X.X.X.X.XXXXXXXXXXXXXXXXXXXXXXXXXXX.X.X.X.X.X.X.X.X.X.X.X.XXXXXXXXXXXXXXXXXXXXXXXXXXX.X.X.X.X.X.X.X.X.X.X.X.XXXXXXXXXXXXXXXXXXXXXXXXXXX.X.X.X.X.X.X.X.X.X.X.X.XXXXXXXXXXXXXXXXXXXXXXXXXXX.X.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX......
#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 s, T;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;}}li lower(li x, li y){return x < 0 ? (x - y + 1) / y : x / y;}int main(){std::scanf("%u", &T);for (ul Case = 1; Case <= T; ++Case) {std::scanf("%u", &s);if (s <= 24) {std::printf("1 %u\n", s + 1);for (ul i = 1; i <= s + 1; ++i) {std::putchar((i & 1) ? 'X' : '.');}std::putchar('\n');} else {li x, y;exgcd(8, 3, x, y);li a = li(s) * x;li b = li(s) * y;li k = lower(b, 8);b -= 8 * k;a += 3 * k;std::printf("25 25\n");for (ul i = 1; i <= 24; ++i) {for (ul j = 1; j <= 25; ++j) {if ((~i & ~j & 1) && a) {std::putchar('.');--a;} else {std::putchar('X');}}std::putchar('\n');}for (ul i = 1; i <= 25; ++i) {if (i <= 25 - b) {std::putchar('X');} else {std::putchar('.');}}std::putchar('\n');}}return 0;}
设表示长为的,满足序列前项要求的,由构成的,以结尾的排列有多少个。
于是可以直接对动态规划。
#include <bits/stdc++.h>using ul = std::uint32_t;using ll = std::int64_t;using ull = std::uint64_t;using li = std::int32_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;}const ul maxn = 5000;ul ans[maxn + 1][maxn + 1];ul T;ul b[maxn];ul n;int main(){std::scanf("%u", &T);for (ul Case = 1; Case <= T; ++Case) {std::scanf("%u", &n);for (ul i = 1; i <= n - 1; ++i) {std::scanf("%u", &b[i]);}ans[1][1] = 1;for (ul x = 1; x <= n - 1; ++x) {for (ul y = 1; y <= x + 1; ++y) {ans[x + 1][y] = 0;}if (b[x]) {for (ul y = 1; y <= x; ++y) {ans[x + 1][1] = plus(ans[x + 1][1], ans[x][y]);ans[x + 1][y + 1] = minus(ans[x + 1][y + 1], ans[x][y]);}} else {for (ul y = 1; y <= x; ++y) {ans[x + 1][y + 1] = plus(ans[x + 1][y + 1], ans[x][y]);}}for (ul y = 1; y <= x + 1; ++y) {ans[x + 1][y] = plus(ans[x + 1][y], ans[x + 1][y - 1]);}}ul out = 0;for (ul y = 1; y <= n; ++y) {out = plus(out, ans[n][y]);}std::printf("%u\n", out);}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 n, k;const ul maxn = 3e5;class node_t {public:ul rk = 0;ul hd = 0;};node_t node[maxn + 1];class edge_t {public:ul to = 0;ul nex = 0;};edge_t edge[(maxn - 1) * 2 + 1];ul totedge;void addedge(ul a, ul b){++totedge;edge[totedge].to = b;edge[totedge].nex = node[a].hd;node[a].hd = totedge;}void getcen(ul curr, ul prev, ul totsz, ul& sz, ul& mnmxsz, ul& mnmxsznid){sz = 1;ul mxsz = 0;for (ul eid = node[curr].hd; eid; eid = edge[eid].nex) {ul nex = edge[eid].to;if (node[nex].rk) {continue;}if (nex == prev) {continue;} else {ul sonsz;getcen(nex, curr, totsz, sonsz, mnmxsz, mnmxsznid);mxsz = std::max(mxsz, sonsz);sz += sonsz;}}mxsz = std::max(mxsz, totsz - sz);if (mnmxsz > mxsz) {mnmxsz = mxsz;mnmxsznid = curr;}}void getsz(ul curr, ul prev, ul& sz){sz = 1;for (ul eid = node[curr].hd; eid; eid = edge[eid].nex) {ul nex = edge[eid].to;if (node[nex].rk) {continue;}if (nex == prev) {continue;} else {ul sonsz;getsz(nex, curr, sonsz);sz += sonsz;}}}std::vector<ul> cnt[maxn + 1];std::vector<ul> sum;ul ans[maxn + 1];void search(ul curr, ul prev, ul rk, ul depth, std::vector<ul>& cnt){if (depth > k) {return;}while (cnt.size() <= depth) {cnt.push_back(0);}++cnt[depth];for (ul eid = node[curr].hd; eid; eid = edge[eid].nex) {ul next = edge[eid].to;if (next == prev || node[next].rk > rk) {continue;}search(next, curr, rk, depth + 1, cnt);}}ul query(const std::vector<ul>& v, ul t){return t < v.size() ? v[t] : v.back();}void update(ul curr, ul prev, ul rk, ul depth, const std::vector<ul>& cnt) {if (depth > k) {return;}++ans[curr];ans[curr] += query(sum, k - depth) - query(cnt, k - depth);for (ul eid = node[curr].hd; eid; eid = edge[eid].nex) {ul next = edge[eid].to;if (next == prev || node[next].rk > rk) {continue;}update(next, curr, rk, depth + 1, cnt);}}ul fa[maxn + 1];void getfather(ul curr, ul prev){fa[curr] = prev;for (ul eid = node[curr].hd; eid; eid = edge[eid].nex) {ul next = edge[eid].to;if (next == prev) {continue;}getfather(next, curr);}}void update1(ul curr, ul prev, ul rk, ul depth, const std::vector<ul>& cnt) {if (depth > k) {return;}ul id = (fa[curr] == prev) ? curr : prev;++ans[id];ans[id] += query(sum, k - depth) - query(cnt, k - depth);if (node[curr].rk > rk) {return;}for (ul eid = node[curr].hd; eid; eid = edge[eid].nex) {ul next = edge[eid].to;if (next == prev) {continue;}update1(next, curr, rk, depth + 1, cnt);}}int main(){std::scanf("%u", &T);for (ul Case = 1; Case <= T; ++Case) {std::scanf("%u%u", &n, &k);totedge = 0;for (ul i = 1; i <= n; ++i) {node[i].hd = 0;node[i].rk = 0;}for (ul i = 1; i <= n - 1; ++i) {ul u, v;u = i;v = i + 1;std::scanf("%u%u", &u, &v);addedge(u, v);addedge(v, u);}for (ul i = 1; i <= n; ++i) {while (!node[i].rk) {ul sz;getsz(i, 0, sz);ul mnmxsz = sz, mnmxsznid;getcen(i, 0, sz, sz, mnmxsz, mnmxsznid);node[mnmxsznid].rk = sz;}}std::memset(ans + 1, 0, sizeof(ul) * n);if (~k & 1) {k >>= 1;for (ul i = 1; i <= n; ++i) {sum.resize(0);sum.resize(1);for (ul eid = node[i].hd; eid; eid = edge[eid].nex) {ul j = edge[eid].to;if (node[j].rk > node[i].rk) {continue;}cnt[j].resize(0);cnt[j].resize(1);search(j, i, node[i].rk, 1, cnt[j]);for (ul l = 0; l != cnt[j].size(); ++l) {while (sum.size() <= l) {sum.push_back(0);}sum[l] += cnt[j][l];if (l) {cnt[j][l] += cnt[j][l - 1];}}}for (ul j = 0; j != sum.size(); ++j) {if (j) {sum[j] += sum[j - 1];}}++ans[i];ans[i] += query(sum, k);for (ul eid = node[i].hd; eid; eid = edge[eid].nex) {ul j = edge[eid].to;if (node[j].rk > node[i].rk) {continue;}update(j, i, node[i].rk, 1, cnt[j]);}}} else {k = k + 1 >> 1;getfather(1, 0);for (ul i = 1; i <= n; ++i) {sum.resize(0);sum.resize(1);for (ul eid = node[i].hd; eid; eid = edge[eid].nex) {ul j = edge[eid].to;cnt[j].resize(0);cnt[j].resize(1);if (node[j].rk > node[i].rk) {continue;}search(j, i, node[i].rk, 1, cnt[j]);for (ul l = 0; l != cnt[j].size(); ++l) {while (sum.size() <= l) {sum.push_back(0);}sum[l] += cnt[j][l];if (l) {cnt[j][l] += cnt[j][l - 1];}}}for (ul j = 0; j != sum.size(); ++j) {if (j) {sum[j] += sum[j - 1];}}for (ul eid = node[i].hd; eid; eid = edge[eid].nex) {ul j = edge[eid].to;update1(j, i, node[i].rk, 1, cnt[j]);}}}ul mx = 0;for (ul i = 1; i <= n; ++i) {mx = std::max(mx, ans[i]);}std::printf("%u\n", n - mx);}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;const ul maxn = 5e4;ul T;ul n;class vec {public:ll x = 0;ll y = 0;vec()=default;vec(ll a, ll b): x(a), y(b) {}};vec v[maxn + 1];class matrix {public:ll xx = 1;ll xy = 0;ll yx = 0;ll yy = 1;matrix()=default;matrix(ll a, ll b, ll c, ll d): xx(a), xy(b), yx(c), yy(d) {}};matrix operator*(const matrix& a, const matrix& b){return matrix(a.xx * b.xx + a.xy * b.yx,a.xx * b.xy + a.xy * b.yy,a.yx * b.xx + a.yy * b.yx,a.yx * b.xy + a.yy * b.yy);}const ll inf = (ll(1) << 63) - 1;class cf {public:matrix prev;ll curr = 0;cf()=default;cf(ll t): curr(t) {}};void getmid(cf& l, cf& r, cf& mid){if (l.curr == inf) {mid = r;mid.curr *= 2;} else if (r.curr == inf) {mid = l;mid.curr *= 2;} else if (l.curr == r.curr + 1) {l.prev = l.prev * matrix(r.curr, 1, 1, 0);r.prev = r.prev * matrix(r.curr, 1, 1, 0);l.curr = 1;r.curr = inf;getmid(l, r, mid);} else if (r.curr == l.curr + 1) {r.prev = r.prev * matrix(l.curr, 1, 1, 0);l.prev = l.prev * matrix(l.curr, 1, 1, 0);r.curr = 1;l.curr = inf;getmid(l, r, mid);} else {mid = l;mid.curr = (l.curr + r.curr) / 2;}}void getfrac(const cf& v, ll& a, ll& b){matrix tmp = v.prev * matrix(v.curr, 1, 1, 0);a = tmp.xx;b = tmp.yx;}ll operator*(const vec& a, const vec& b){return a.x * b.x + a.y * b.y;}void exgcd(ll a, ll b, ll& x, ll& y){if (b) {exgcd(b, a % b, y, x);y -= a / b * x;} else {x = 1;y = 0;}}int main(){std::scanf("%u", &T);for (ul Case = 1; Case <= T; ++Case) {std::scanf("%u", &n);for (ul i = 1; i <= n; ++i) {std::scanf("%lld%lld", &v[i].x, &v[i].y);}std::sort(v + 1, v + n + 1, [](const vec& a, const vec& b){return a.y != b.y ? a.y < b.y : a.x < b.x;});ul m = n >> 1;if (v[m].y != v[m + 1].y) {std::printf("-1000000000 %lld 1000000000 %lld\n", v[m + 1].y, v[m].y);} else {std::printf("%lld %lld %lld %lld\n", v[m].x - 10000000, v[m].y + 1, v[m + 1].x + 10000000, v[m].y - 1);}ul t = n >> 2;cf l(-1e9), r(1e9);cf mid;vec dir;while (true) {getmid(l, r, mid);getfrac(mid, dir.y, dir.x);std::sort(v + 1, v + m + 1, [&](const vec& a, const vec& b){return a * dir < b * dir;});std::sort(v + m + 1, v + n + 1, [&](const vec& a, const vec& b){return a * dir < b * dir;});if (v[t + 1] * dir <= v[m + t] * dir) {r = mid;} else if (v[m + t + 1] * dir <= v[t] * dir) {l = mid;} else if (v[t + 1] * dir == v[m + t] * dir + 1) {r = mid;} else if (v[m + t + 1] * dir == v[t] * dir + 1) {l = mid;} else {break;}}ll x, y;if (dir.y >= 0) {exgcd(dir.x, dir.y, x, y);} else {exgcd(dir.x, -dir.y, x, y);y = -y;}vec tmp = v[m + t] * dir > v[t] * dir ? v[m + t] : v[t];tmp.x += x;tmp.y += y;vec tmp2(tmp.x - dir.y, tmp.y + dir.x);std::printf("%lld %lld %lld %lld\n", tmp.x, tmp.y, tmp2.x, tmp2.y);}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;const ul maxn = 5e6;ul n, m;ul a[maxn + 1];ul b[maxn + 1];constexpr int threshold = 10000000;unsigned long long k1, k2;unsigned long long xorShift128Plus() {unsigned long long k3 = k1, k4 = k2;k1 = k4;k3 ^= (k3 << 23);k2 = k3 ^ k4 ^(k3 >> 17) ^ (k4 >> 26);return k2 + k4;}void gen(int n, unsigned long long _k1, unsigned long long _k2) {k1 = _k1, k2 = _k2;for (int i = 1; i <= n; i++) {a[i] = xorShift128Plus() % threshold + 1;b[i] = xorShift128Plus() % threshold + 1;}}int main(){while (std::scanf("%u%u%llu%llu", &n, &m, &k1, &k2) > 0) {gen(n, k1, k2);for (ul i = 1; i <= n; ++i) {b[i] += a[i];}std::sort(a + 1, a + n + 1);std::reverse(a + 1, a + n + 1);std::sort(b + 1, b + n + 1);std::reverse(b + 1, b + n + 1);ul ia = 0, ib = 0;ull sum = 0, ans = 0;for (ul i = 1; i <= m; ++i) {if (i > n + n + n) {} else if (ia == n) {sum -= a[ia];--ia;++ib;sum += b[ib];} else if (ia == 0 || ib == n) {++ia;sum += a[ia];} else {ull s1 = sum + a[ia + 1];ull s2 = sum - a[ia] + b[ib + 1];if (s1 > s2) {sum = s1;++ia;} else {sum = s2;--ia;++ib;}}ans ^= sum;}std::printf("%llu\n", ans);}return 0;}
本题需要用到竞赛图的这样一个性质:对任何的竞赛图,一定存在某个,满足可以将其点集分为这些组,其中所有,且每个都是一个强连通分量,并且对任何,和之间的所有边,一定是从到的,并且内部一定存在哈密顿圈。
关于这一点的证明,考虑下述构造方式:考虑一个点一个点添加,假设已经添加的点已经被整理成了上述结构,那么新来的点有以下几种可能性:
1、不存在从已有点到新来点的边,于是新来点独自作为一个新的强连通分量排在首位;
2、不存在从新来点到已有点的边,于是新来点独自作为一个新的强连通分量排在首位;
对于剩下的情况,用表示能到达新来点的强连通分量中编号最大的,用表示新来点能到达的强连通分量中编号最小的:
3、(此时必然),那么新来点独自作为一个新的强连通分量插入它俩之间;
4、,那么既然新来点既能到此分量又能被此分量到达,则一定存在一个合适的插入位置更新此分量的哈密顿圈;
5、,任意从中选一个新来点能一步到达的点,从它出发把中的边全部按照原哈密顿圈走一遍,再把中的点随便找个开头按照原哈密顿圈走一遍,最后任意从中选一个能一步到达新来点的点,从其之后那个点开始,按照原哈密顿圈走一遍,以之结束,最后在走到新来点。于是就成功吧中的点以及新来点重新组织为一个哈密顿圈,把这些强连通分量合并。
如上过程就是一个算法,于是考虑已经将输入的竞赛图整理为如上形式。现在如果,则无论如何不会强连通(特判);如果且,那么一条边被改方向之后强连通当且仅当其是从到的;如果,那么原先强连通,由于已经有了一个哈密顿圈,改变圈外边的方向显然不会造成不强连通,现在考虑圈上的边,如果将其改变方向会使得不再强连通,则圈上一定存在某,满足圈上从到的所有点”和“圈上从到的所有点”之间的所有边(除了)都是从后者到前者的,于是枚举判断就是,需要做到对每个实现判断,这一点只要注意到,在重新对点标号之后,这只是个区间求和问题。
#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>;ul T;const ul maxn = 5e3;ul n;char s[maxn + 1];ul edge[maxn + 1][maxn + 1];ul sum[maxn + 1][maxn + 1];ul ans[maxn + 1][maxn + 1];ul val[256];char rval[16];vul scc[maxn + 1];ul scccnt;std::vector<ul> tmp;ul id[maxn + 1];ul query(ul u, ul d, ul l, ul r){return sum[d][r] + sum[u - 1][l - 1] - sum[u - 1][r] - sum[d][l - 1];}int main(){for (ul i = 0; i <= 9; ++i) {val[i + '0'] = i;rval[i] = i + '0';}for (ul i = 0; i <= 5; ++i) {val[i + 'A'] = i + 10;rval[i + 10] = i + 'A';}std::scanf("%u", &T);for (ul Case = 1; Case <= T; ++Case) {std::scanf("%u", &n);for (ul i = 1; i <= n; ++i) {for (ul j = 1; j <= n; ++j) {edge[i][j] = 0;}}for (ul i = 1; i <= n - 1; ++i) {std::scanf("%s", s + 1);for (ul j = 1; s[j]; ++j) {ul v = val[s[j]];for (ul k = 0; k <= 3 && (j << 2) + k - 3 <= i; ++k) {if (v >> k & 1) {edge[i + 1][(j << 2) + k - 3] = 1;} else {edge[(j << 2) + k - 3][i + 1] = 1;}}}}if (n == 2) {std::printf("0\n");continue;}scccnt = 0;for (ul i = 1; i <= n; ++i) {ul toi = 0, ito = 0;for (ul j = 1; j <= scccnt; ++j) {for (ul k : scc[j]) {if (!ito && edge[i][k]) {ito = j;}if (edge[k][i]) {toi = j;}}}if (!ito) {++scccnt;scc[scccnt].resize(0);scc[scccnt].push_back(i);} else if (ito > toi) {++scccnt;for (ul j = scccnt; j > ito; --j) {std::swap(scc[j - 1], scc[j]);}scc[ito].resize(0);scc[ito].push_back(i);} else if (ito == toi) {if (edge[scc[toi].back()][i] && edge[i][scc[ito].front()]) {scc[ito].push_back(i);} else {tmp.resize(0);tmp.push_back(i);for (ul k = 1; k != scc[ito].size(); ++k) {if (edge[scc[toi][k - 1]][i] && edge[i][scc[ito][k]]) {for (ul j = k; j != scc[ito].size(); ++j) {tmp.push_back(scc[ito][j]);}for (ul j = 0; j != k; ++j) {tmp.push_back(scc[ito][j]);}std::swap(tmp, scc[ito]);break;}}}} else {tmp.resize(0);tmp.push_back(i);for (ul k = 0; k != scc[ito].size(); ++k) {if (edge[i][scc[ito][k]]) {for (ul j = k; j != scc[ito].size(); ++j) {tmp.push_back(scc[ito][j]);}for (ul j = 0; j != k; ++j) {tmp.push_back(scc[ito][j]);}break;}}for (ul j = ito + 1; j < toi; ++j) {for (ul k : scc[j]) {tmp.push_back(k);}}for (ul k = 0; k != scc[toi].size(); ++k) {if (edge[scc[toi][k]][i]) {for (ul j = k + 1; j != scc[toi].size(); ++j) {tmp.push_back(scc[toi][j]);}for (ul j = 0; j <= k; ++j) {tmp.push_back(scc[toi][j]);}break;}}scccnt -= toi - ito;std::swap(scc[ito], tmp);for (ul j = ito + 1; j <= scccnt; ++j) {std::swap(scc[j], scc[j + toi - ito]);}}}if (scccnt != 1) {for (ul i = 1; i <= n; ++i) {for (ul j = 1; j <= n; ++j) {ans[i][j] = 0;}}for (ul k1 : scc[1]) {for (ul k2 : scc[scccnt]) {ans[k1][k2] = 1;ans[k2][k1] = 1;}}} else {for (ul i = 1; i <= n; ++i) {for (ul j = 1; j <= n; ++j) {ans[i][j] = 1;}}for (ul i = 1; i <= n; ++i) {id[i] = scc[1][i - 1];}for (ul i = 1; i <= n; ++i) {for (ul j = 1; j <= n; ++j) {sum[i][j] = edge[id[i]][id[j]] + sum[i][j - 1];}}for (ul i = 1; i <= n; ++i) {for (ul j = 1; j <= n; ++j) {sum[j][i] += sum[j - 1][i];}}for (ul i = 1, j = 2; j <= n; ++i, ++j) {bool flag = true;for (ul s = j, t = j + 1; t <= n && flag; ++s, ++t) {ul tmp = query(j, s, t, n) + query(j, s, 1, i);if (tmp == (s - j + 1) * (n - t + 1 + i) - 1) {flag = false;}}if (flag) {ul s = n, t = 1;ul tmp = query(j, s, t, i);if (tmp == (s - j + 1) * (i - t + 1) - 1) {flag = false;}}for (ul s = 1, t = 2; t <= i && flag; ++s, ++t) {ul tmp = query(j, n, t, i) + query(1, s, t, i);if (tmp == (n - j + 1 + s) * (i - t + 1) - 1) {flag = false;}}if (!flag) {ans[id[i]][id[j]] = 0;ans[id[j]][id[i]] = 0;}}for (ul s = 1, t = 2; t <= n; ++s, ++t) {ul tmp = query(1, s, t, n);if (tmp == s * (n - t + 1) - 1) {ans[id[1]][id[n]] = 0;ans[id[n]][id[1]] = 0;break;}}}for (ul i = 1; i <= n - 1; ++i) {for (ul j = 1; (j << 2) - 3 <= i; ++j) {ul v = 0;for (ul k = 0; k <= 3 && (j << 2) + k - 3 <= i; ++k) {v |= ans[i + 1][(j << 2) + k - 3] << k;}std::putchar(rval[v]);}std::putchar('\n');}}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>;using lf = double;using llf = long double;ul T;ul n;const ul maxn = 1e6;ul p;ll x[maxn + 1], y[maxn + 1];ll X1,Y1,X2,Y2,Z;llf sqr(llf x){return x * x;}llf mysqrt(llf x){return std::sqrt(std::max(llf(0), x));}llf calc(llf a, llf b, llf c){return mysqrt((a + c + mysqrt(sqr(a + c) - llf(4) * (a * c - b * b))) / llf(2));}int main(){std::scanf("%u", &T);for (ul Case = 1; Case <= T; ++Case) {std::scanf("%u", &n);p = 0;X1 = X2 = Y1 = Y2 = Z = 0;for (ul i = 1; i <= n; ++i) {ul q;std::scanf("%u", &q);if (q == 1) {std::scanf("%lld%lld", &x[i], &y[i]);X1 += x[i];X2 += x[i] * x[i];Y1 += y[i];Y2 += y[i] * y[i];Z += x[i] * y[i];++p;} else {ul t;std::scanf("%u", &t);X1 -= x[t];X2 -= x[t] * x[t];Y1 -= y[t];Y2 -= y[t] * y[t];Z -= x[t] * y[t];--p;}lf tmp = calc(llf(2) * (llf(X2) / llf(p) - sqr(llf(X1) / llf(p))), llf(2) * (llf(Z) / llf(p) - (llf(X1) / llf(p)) * (llf(Y1) / llf(p))), llf(2) * (llf(Y2) / llf(p) - sqr(llf(Y1) / llf(p))));std::printf("%.20lf\n", tmp);}}return 0;}
AB二人一开始必然选择不同行不同列的两个格子拿完,否则B立即就输了。将与AB二人所拿两格同行或同列的格子编号为,另一格编号为,于是现在问题变为,谁先使得中任何一堆石子空掉,谁就输了,其他情况均不会立即输。于是现在该游戏等价于大小为的一般抓石子游戏。
#include <bits/stdc++.h>using ul = std::uint32_t;ul T;ul v[3][3];int main(){std::scanf("%u", &T);for (ul Case = 1; Case <= T; ++Case) {for (ul i = 0; i != 3; ++i) {for (ul j = 0; j != 3; ++j) {std::scanf("%u", &v[i][j]);}}ul ans = 0;for (ul ai = 0; ai != 3; ++ai) {for (ul aj = 0; aj != 3; ++aj) {bool flag = true;for (ul bi = 0; bi != 3 && flag; ++bi) {if (bi == ai) {continue;}for (ul bj = 0; bj != 3 && flag; ++bj) {if (bj == aj) {continue;}ul t = 0;for (ul ci = 0; ci != 3; ++ci) {for (ul cj = 0; cj != 3; ++cj) {if (ci == ai && cj == aj) {continue;}if (ci == bi && cj == bj) {continue;}if (ci != ai && cj != aj && ci != bi && cj != bj) {t ^= v[ci][cj];} else {t ^= v[ci][cj] - 1;}}}if (!t) {flag = false;}}}if (flag) {++ans;}}}std::printf("%u\n", ans);}return 0;}
只要证明且时:
#include <bits/stdc++.h>using ul = std::uint32_t;ul T;const ul maxn = 100;ul n, m, k;class worker {public:ul id = 0;ul cnt = 0;};bool operator<(const worker& a, const worker& b){return a.cnt != b.cnt ? a.cnt > b.cnt : a.id < b.id;}worker t[maxn + 1];int main(){std::scanf("%u", &T);for (ul Case = 1; Case <= T; ++Case) {std::scanf("%u%u%u", &n, &m, &k);for (ul i = 1; i <= n; ++i) {std::scanf("%u", &t[i].cnt);t[i].id = i;}if (k > 0) {std::sort(t + 1, t + n + 1);}for (ul i = 1; i <= n; ++i) {if (i != 1) {std::putchar(' ');}std::printf("%u", t[i].id);}std::putchar('\n');}return 0;}