HNOI2014解题报告
Author: Pengyihao
Day1 T1 画框
思路
这个题目我其实是没有思路的。
网上说要用一种高深的最小乘积生成树的算法,我就学了一下。
我们把每一种搭配方案中,
A
的和记做
那么一种搭配方案就可以看做一个坐标 (x,y) 。
因为 disharmony=x⋅y ,所以我们可以把 disharmony 看作反比例函数的 k 。
因为反比例函数越靠近原点,
所以我们要 (x,y) 尽可能靠近原点。
找到
x
最小的方案
为什么大家都用KM算法,就我用费用流?好怕怕啊
然后我们就要找到在这两个方案的坐标的连线的下方,且三个方案形成的三角形面积最大的方案。
这个面积用叉积可以计算,最后发现形如 aA+bB ,其中 A,B 是题目中的那个东西。
于是我们可以再跑一遍费用流求出这个方案 mid 。
递归处理 (l,mid) 和 (mid,r) 。
边界就是 l==mid||mid==r 。
这个据说递归次数期望是 lnn‾‾‾‾√ ,然后复杂度就对了。
代码
#include <bits/stdc++.h>
typedef long long LL;
#define FOR(i, a, b) for (LL i = (a), i##_END_ = (b); i <= i##_END_; i++)
#define DNF(i, a, b) for (LL i = (a), i##_END_ = (b); i >= i##_END_; i--)
template <typename Tp> void in(Tp &x) {
char ch = getchar(); x = 0;
while (ch < '0' || ch > '9') ch = getchar();
while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
}
template <typename Tp> Tp Min(Tp x, Tp y) {return x < y ? x : y;}
template <typename Tp> Tp Max(Tp x, Tp y) {return x > y ? x : y;}
template <typename Tp> Tp chkmax(Tp &x, Tp y) {return x > y ? x : x=y;}
template <typename Tp> Tp chkmin(Tp &x, Tp y) {return x < y ? x : x=y;}
const LL MAXN = 80;
LL n, A[MAXN][MAXN], B[MAXN][MAXN], tmp[MAXN][MAXN];
namespace KM
{
using std::queue; queue<LL>q;
bool in_stack[MAXN << 1];
const LL ss = 0, tt = 159;
LL cnt, ret1, ret2, dis[MAXN << 1], pre[MAXN << 1];
LL head[MAXN << 1], data[MAXN * MAXN << 2], flow[MAXN * MAXN << 2];
LL wei1[MAXN * MAXN << 2], wei2[MAXN * MAXN << 2], nxt[MAXN * MAXN << 2], wei[MAXN * MAXN << 2];
void add(LL x, LL y, LL z, LL l, LL l1, LL l2)
{
nxt[cnt] = head[x]; data[cnt] = y; flow[cnt] = z;
wei[cnt] = l; wei1[cnt] = l1; wei2[cnt] = l2; head[x] = cnt++;
nxt[cnt] = head[y]; data[cnt] = x; flow[cnt] = 0;
wei[cnt] =-l; wei1[cnt] =-l1; wei2[cnt] =-l2; head[y] = cnt++;
}
bool bfs()
{
memset(pre, -1, sizeof pre);
memset(dis, 0x3f, sizeof dis);
dis[ss] = 0; q.push(ss); in_stack[ss] = true;
while (!q.empty()) {
LL now = q.front(); q.pop();
in_stack[now] = false;
for (LL i = head[now]; i != -1; i = nxt[i]) {
if (flow[i] && dis[data[i]] > dis[now] + wei[i]) {
dis[data[i]] = dis[now] + wei[i];
pre[data[i]] = i;
if (!in_stack[data[i]]) {
in_stack[data[i]] = true;
q.push(data[i]);
}
}
}
}
return pre[tt] != -1;
}
void dfs()
{
for (LL i = tt; pre[i] != -1; i = data[pre[i] ^ 1]) ret1 += wei1[pre[i]];
for (LL i = tt; pre[i] != -1; i = data[pre[i] ^ 1]) ret2 += wei2[pre[i]];
for (LL i = tt; pre[i] != -1; i = data[pre[i] ^ 1]) flow[pre[i]]--, flow[pre[i] ^ 1]++;
}
std::pair<LL, LL>main(LL argv[MAXN][MAXN])
{
ret1 = ret2 = 0; cnt = 0;
memset(head, -1, sizeof head);
FOR(i, 1, n) add(ss, i, 1, 0, 0, 0);
FOR(i, 1, n) FOR(j, 1, n) {
add(i, j + n, 1, argv[i][j], A[i][j], B[i][j]);
}
FOR(i, 1, n) add(i + n, tt, 1, 0, 0, 0);
while (bfs()) dfs();
return std::make_pair(ret1, ret2);
}
}
bool gongxian(std::pair<LL, LL>l, std::pair<LL, LL>mid, std::pair<LL, LL>r)
{
return (l.second - r.second) * (l.first - mid.first) == (l.second - mid.second) * (l.first - r.first);
}
LL get_ans(std::pair<LL, LL>l, std::pair<LL, LL>r)
{
if (l.first == r.first || l.second == r.second)
return Min(l.first * l.second, r.first * r.second);
FOR(i, 1, n) FOR(j, 1, n)
tmp[i][j] = -((r.second - l.second) * A[i][j] + (l.first - r.first) * B[i][j]);
std::pair<LL, LL>mid = KM::main(tmp);
if (gongxian(l, mid, r)) return Min(l.first * l.second, r.first * r.second);
return Min(get_ans(l, mid), get_ans(mid, r));
}
int main()
{
freopen("frame.in", "r", stdin);
freopen("frame.out", "w", stdout);
LL tcase; in(tcase);
while (tcase--) {
in(n);
FOR(i, 1, n) FOR(j, 1, n) in(A[i][j]);
FOR(i, 1, n) FOR(j, 1, n) in(B[i][j]);
std::pair<LL, LL>retA = KM::main(A), retB = KM::main(B);
printf("%lld\n", get_ans(retA, retB));
}
return 0;
}
Day1 T2 世界树
思路
这也是我没学过的算法——虚树。
首先用单调栈维护右链,把虚树构建出来;
然后对于虚树上的每条边,把它的分界点找出来,然后分段赋给管理端点的那两个点。
注意判断管理端点的两个点相同的情况。
这个题目的构建虚树需要用lca,我用了倍增。
这个题目找到分界点也需要用倍增。
没有使用数据结构,代码却比数据结构题还要长。
代码
#include <bits/stdc++.h>
typedef long long LL;
#define debug(...) fprintf(stderr, __VA_ARGS__)
#define FOR(i, a, b) for (int i = (a), i##_END_ = (b); i <= i##_END_; i++)
#define DNF(i, a, b) for (int i = (a), i##_END_ = (b); i >= i##_END_; i--)
template <typename Tp> void in(Tp &x) {
char ch = getchar(); x = 0;
while (ch < '0' || ch > '9') ch = getchar();
while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
}
template <typename Tp> Tp Min(Tp x, Tp y) {return x < y ? x : y;}
template <typename Tp> Tp Max(Tp x, Tp y) {return x > y ? x : y;}
template <typename Tp> Tp chkmax(Tp &x, Tp y) {return x > y ? x : x=y;}
template <typename Tp> Tp chkmin(Tp &x, Tp y) {return x < y ? x : x=y;}
const int MAXN = 300010;
bool is_chs[MAXN];
int n, q, num[MAXN], fa[MAXN], dfn[MAXN], sa[MAXN], depth[MAXN], sz[MAXN];
int INDEX, cnt, head[MAXN], data[MAXN << 1], nxt[MAXN << 1], log_num[1000010];
int anc[MAXN][20], xsz[MAXN], val[MAXN];
void add(int x, int y)
{
nxt[cnt] = head[x]; data[cnt] = y; head[x] = cnt++;
nxt[cnt] = head[y]; data[cnt] = x; head[y] = cnt++;
}
void dfs(int now, int pa)
{
sz[now] = 1;
depth[now] = depth[pa] + 1;
fa[now] = pa; dfn[now] = ++INDEX;
for (int i = head[now]; i != -1; i = nxt[i]) {
if (data[i] != pa) {
dfs(data[i], now);
sz[now] += sz[data[i]];
}
}
}
void dfs_anc(int now, int pa)
{
anc[now][0] = pa;
for (int i = 1; anc[now][i - 1] != -1; i++) {
anc[now][i] = anc[anc[now][i - 1]][i - 1];
}
for (int i = head[now]; i != -1; i = nxt[i]) {
if (data[i] != pa) dfs_anc(data[i], now);
}
}
namespace get_lca
{
int eula[MAXN * 3], st[MAXN], ed[MAXN], IND, to[MAXN];
int minx[MAXN * 3][21];
void dfs2(int now)
{
eula[++eula[0]] = ++IND;
st[now] = eula[0]; to[IND] = now;
for (int i = head[now]; i != -1; i = nxt[i]) {
if (data[i] != fa[now]) {
dfs2(data[i]);
eula[++eula[0]] = eula[st[now]];
}
}
ed[now] = eula[0];
}
int lca(int x, int y)
{
if (!x || !y) return 0;
if (st[x] > ed[y]) std::swap(x, y); x = st[x]; y = ed[y];
int delta = log_num[y - x + 1];
return to[Min(minx[x][delta], minx[y - (1 << delta) + 1][delta])];
}
void start()
{
FOR(i, 1, eula[0]) minx[i][0] = eula[i];
FOR(i, 1, 20) {
FOR(j, 1, eula[0]) {
if (j + (1 << i) - 1 > eula[0]) break;
minx[j][i] =
Min(minx[j][i - 1], minx[j + (1 << (i - 1))][i - 1]);
}
}
FOR(i, 1, 1000000) log_num[i] = log2(i);
}
}
int stack[MAXN], top, in_tree[MAXN];
int head_xu[MAXN], cnt_xu, nxt_xu[MAXN << 1], data_xu[MAXN << 1];
namespace build_tree
{
void add_xu(int x, int y)
{
nxt_xu[cnt_xu] = head_xu[x]; data_xu[cnt_xu] = y; head_xu[x] = cnt_xu++;
nxt_xu[cnt_xu] = head_xu[y]; data_xu[cnt_xu] = x; head_xu[y] = cnt_xu++;
}
bool cmp(int x, int y)
{
return dfn[x] < dfn[y];
}
void builds_tree()
{
cnt_xu = 0;
stack[top = 1] = 0;
in_tree[0] = 0;
FOR(i, 1, num[0]) {
is_chs[num[i]] = true;
in_tree[++in_tree[0]] = num[i];
}
in_tree[++in_tree[0]] = 0;
std::sort(in_tree + 1, in_tree + in_tree[0] + 1, cmp);
int now_in_tree = in_tree[0];
FOR(i, 2, now_in_tree) {
int now = in_tree[i];
int anc = get_lca::lca(stack[top], now);
if (anc == stack[top]) stack[++top] = now;
else {
while (true) {
int tp = stack[top], tp_l = stack[top - 1];
if (tp_l == anc) {
add_xu(tp_l, tp); top--; break;
}
else if (dfn[tp_l] > dfn[anc]) {
add_xu(tp_l, tp); top--;
}
else {
in_tree[++in_tree[0]] = anc;
add_xu(anc, tp); stack[top] = anc; break;
}
}
stack[++top] = now;
}
}
while (top != 1) {
add_xu(stack[top - 1], stack[top]); top--;
}
}
}
namespace find_father
{
int find(int x, int y)
{
for (int i = 19; i >= 0; i--) {
if (y & (1 << i)) {
x = anc[x][i];
}
}
return x;
}
}
namespace DP
{
int f[MAXN][2], g[MAXN][2], ret[MAXN];
void dp1(int now, int pa)
{
val[now] = 0;
xsz[now] = 1;
f[now][0] = is_chs[now] ? 0 : 0x3f3f3f3f;
f[now][1] = now;
for (int i = head_xu[now]; i != -1; i = nxt_xu[i]) {
if (data_xu[i] != pa) {
dp1(data_xu[i], now);
xsz[now] += xsz[data_xu[i]];
int frm = depth[data_xu[i]] - depth[now];
if (f[now][0] > f[data_xu[i]][0] + frm ||
f[now][0] == f[data_xu[i]][0] + frm &&
f[now][1] > f[data_xu[i]][1])
{
f[now][1] = f[data_xu[i]][1];
f[now][0] = f[data_xu[i]][0] + frm;
}
}
}
}
void dp2(int now, int pa)
{
g[now][0] = f[now][0];
g[now][1] = f[now][1];
if (pa != -1) {
int frm = depth[now] - depth[pa];
if (g[now][0] > g[pa][0] + frm ||
g[now][0] == g[pa][0] + frm && g[now][1] > g[pa][1])
{
g[now][1] = g[pa][1]; g[now][0] = g[pa][0] + frm;
}
}
for (int i = head_xu[now]; i != -1; i = nxt_xu[i]) {
if (data_xu[i] != pa) dp2(data_xu[i], now);
}
}
int getr(int x, int y, int z, bool t)
{
if (y - x + z < 0) return 0;
if (t) return (y - x + z) / 2;
else {
if ((y - x + z) % 2 == 0) return (y - x + z) / 2;
else return (y - x + z) / 2 + 1;
}
}
void dp3(int now, int pa)
{
if (!pa) {
ret[g[now][1]] += n - sz[now];
}
bool flag = false;
for (int i = head_xu[now]; i != -1; i = nxt_xu[i])
if (data_xu[i] != pa) {
flag = true;
int nnum = data_xu[i], all;
if (now) {
all = depth[nnum] - depth[now] - 1;
if (g[now][1] == g[nnum][1]) {
if (all) {
int pos = find_father::find(nnum, all);
ret[g[now][1]] += sz[pos] - sz[nnum];
val[now] -= sz[pos];
}
else val[now] -= sz[nnum];
}
else {
// ret[g[now][1]] += getr(g[now][0], g[nnum][0], all,
// g[now][1] > g[nnum][1]);
// ret[g[nnum][1]] += getr(g[nnum][0], g[now][0], all,
// g[nnum][1] > g[now][1]);
if (!all) {
val[now] -= sz[nnum];
}
else {
int Anc = getr(g[nnum][0], g[now][0], all,
g[nnum][1] > g[now][1]);
int pos = find_father::find(nnum, Anc);
ret[g[nnum][1]] += sz[pos] - sz[nnum];
int pos2 = find_father::find(nnum, all);
ret[g[now][1]] += sz[pos2] - sz[pos];
val[now] -= sz[pos2];
}
}
}
dp3(nnum, now);
}
}
void dp4(int now, int pa)
{
if (now) ret[g[now][1]] += sz[now] + val[now];
for (int i = head_xu[now]; i != -1; i = nxt_xu[i]) {
if (data_xu[i] != pa) dp4(data_xu[i], now);
}
}
void work()
{
build_tree::builds_tree();
FOR(i, 1, num[0]) ret[num[i]] = 0;
dp1(0, -1); dp2(0, -1); dp3(0, -1); dp4(0, -1);
FOR(i, 1, num[0]) printf("%d ", ret[num[i]]);
putchar(10);
}
}
int main()
{
freopen("worldtree.in", "r", stdin);
freopen("worldtree.out", "w", stdout);
memset(head, -1, sizeof head);
memset(head_xu, -1, sizeof head_xu);
in(n);
FOR(i, 1, n - 1) {
int x, y; in(x); in(y); add(x, y);
}
memset(anc, -1, sizeof anc);
dfs(1, 0); dfs_anc(1, 0);
get_lca::dfs2(1); get_lca::start();
FOR(i, 1, n) sa[dfn[i]] = i;
in(q);
FOR(i, 1, q) {
FOR(i, 1, num[0])
is_chs[num[i]] = false;
FOR(i, 1, in_tree[0])
head_xu[in_tree[i]] = -1;
in(num[0]);
FOR(j, 1, num[0]) in(num[j]);
DP::work();
}
return 0;
}
Day1 T3 米特运输
思路
根据每个点的权值,计算出根节点的权值。
然后用 n− 根节点权值的众数即可。
因为数据太大,所以需要取对数。
代码
#include <bits/stdc++.h>
typedef long long LL;
#define FOR(i, a, b) for (int i = (a), i##_END_ = (b); i <= i##_END_; i++)
#define DNF(i, a, b) for (int i = (a), i##_END_ = (b); i >= i##_END_; i--)
template <typename Tp> void in(Tp &x) {
char ch = getchar(), f = 1; x = 0;
while (ch != '-' && (ch < '0' || ch > '9')) ch = getchar();
if (ch == '-') f = -1, ch = getchar();
while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
x *= f;
}
template <typename Tp> Tp chkmax(Tp &x, Tp y) {return x > y ? x : x=y;}
template <typename Tp> Tp chkmin(Tp &x, Tp y) {return x < y ? x : x=y;}
template <typename Tp> Tp Max(Tp x, Tp y) {return x > y ? x : y;}
template <typename Tp> Tp Min(Tp x, Tp y) {return x < y ? x : y;}
const int MAXN = 500010;
long double rot[MAXN], val[MAXN];
int n, w[MAXN], tot[MAXN];
int cnt, head[MAXN], data[MAXN << 1], nxt[MAXN << 1];
void add(int x, int y)
{
nxt[cnt] = head[x]; data[cnt] = y; head[x] = cnt++;
nxt[cnt] = head[y]; data[cnt] = x; head[y] = cnt++;
}
void dfs1(int now, int pa)
{
for (int i = head[now]; i != -1; i = nxt[i])
if (data[i] != pa) {
tot[now]++;
dfs1(data[i], now);
}
}
void dfs2(int now, int pa)
{
for (int i = head[now]; i != -1; i = nxt[i]) {
if (data[i] != pa) {
val[data[i]] = log(tot[now]) + val[now];
dfs2(data[i], now);
}
}
}
const long double eps = 1e-6;
int main()
{
freopen("meat.in", "r", stdin);
freopen("meat.out", "w", stdout);
memset(head, -1, sizeof head);
in(n);
FOR(i, 1, n) in(w[i]);
FOR(i, 1, n - 1) {int u, v; in(u); in(v); add(u, v);}
dfs1(1, 0); dfs2(1, 0);
FOR(i, 1, n) rot[i] = val[i] + log(w[i]);
std::sort(rot + 1, rot + n + 1);
int ret = 0, line = 0;
FOR(i, 1, n) {
if (i == 1 || fabs(rot[i] - rot[i - 1]) > eps) {
chkmax(ret, line);
line = 1;
}
else line++;
}
chkmax(ret, line);
printf("%d\n", n - ret);
return 0;
}
Day2 T1 抄卡组
思路
首先,如果所有的字符串都有通配符,那么只需要两两比较前缀和后缀即可。
就是按每个字符串不包含通配符的最大前缀长度进行排序,比较一遍;
然后按每个字符串不包含通配符的最大后缀长度进行排序,比较一边。
如果所有的字符串都没有通配符,那么只需要比较hash值就可以了。
如果有些有通配符,有些没有通配符,将没有通配符的进行比较,看是否相同。
然后对于有通配符的,将通配符视作分隔符,即把原串分为一段一段的,分开进行匹配。
注意有通配符的字符串和没有通配符的字符串的首尾要对应。
代码
#include <bits/stdc++.h>
typedef long long LL;
typedef unsigned long long ULL;
#define FOR(i, a, b) for (int i = (a), i##_END_ = (b); i <= i##_END_; i++)
#define DNF(i, a, b) for (int i = (a), i##_END_ = (b); i >= i##_END_; i--)
template <typename Tp> void in(Tp &x) {
char ch = getchar(), f = 1; x = 0;
while (ch != '-' && (ch < '0' || ch > '9')) ch = getchar();
if (ch == '-') f = -1, ch = getchar();
while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
x *= f;
}
template <typename Tp> Tp chkmax(Tp &x, Tp y) {return x > y ? x : x=y;}
template <typename Tp> Tp chkmin(Tp &x, Tp y) {return x < y ? x : x=y;}
template <typename Tp> Tp Max(Tp x, Tp y) {return x > y ? x : y;}
template <typename Tp> Tp Min(Tp x, Tp y) {return x < y ? x : y;}
const int MAXN = 2000010;
using std::string;
using std::cin;
using std::cout;
string str[MAXN];
int t, n, len[MAXN];
ULL hs[MAXN];
const ULL BASE = 31;
int prefix[MAXN], suffix[MAXN], numb[MAXN];
void gethash(int num)
{
hs[num] = 0;
FOR(i, 0, len[num] - 1) {
hs[num] = hs[num] * BASE + str[num][i];
}
}
bool isop[MAXN];
int nopera[MAXN];
ULL rhs[MAXN], po[MAXN];
ULL power(ULL x, ULL y)
{
if (!y) return 1;
return po[y];
}
void work()
{
string *a = str;
FOR(i, 1, nopera[0])
isop[nopera[i]] = false;
nopera[0] = 0;
FOR(i, 1, n) {
bool flag = true;
FOR(j, 0, len[i] - 1) {
if (str[i][j] == '*') {
flag = false;
break;
}
}
if (flag) {
isop[i] = true;
nopera[++nopera[0]] = i;
}
}
FOR(i, 2, nopera[0]) {
if (hs[nopera[i]] != hs[nopera[i - 1]]) {
puts("N");
return;
}
}
int need_compare = nopera[1];
ULL TBASE = 1;
FOR(i, 1, len[need_compare]) {
rhs[i] = rhs[i - 1] + str[need_compare][i - 1] * TBASE;
TBASE = TBASE * BASE;
}
FOR(i, 1, n) if (!isop[i]) {
int lastpos = 0;
for (int j = 0, k; j <= len[i] - 1; j = k + 1) {
k = j;
while (k < len[i] && str[i][k] == '*') k++;
j = k;
while (k < len[i] && str[i][k] != '*') k++; k--;
if (str[i][k] == '*') break;
ULL nowhs = 0, TBASE = 1;
FOR(l, j, k) {
nowhs = nowhs + str[i][l] * TBASE;
TBASE = TBASE * BASE;
}
while (true) {
if (lastpos + (k - j + 1) > len[need_compare]) {
puts("N");
return;
}
if (k == len[i] - 1) {
lastpos = len[need_compare] - (k - j + 1);
}
ULL tmpnow = nowhs;
if (rhs[lastpos + (k - j + 1)] - rhs[lastpos] == tmpnow * power(31, lastpos)) {
lastpos += (k - j + 1);
break;
}
if (j != 0) {
lastpos++;
}
else {
puts("N");
return;
}
}
}
}
puts("Y");
}
bool cmp_prefix(int x, int y)
{
return prefix[x] < prefix[y];
}
bool cmp_suffix(int x, int y)
{
return suffix[x] < suffix[y];
}
int main()
{
freopen("hs.in", "r", stdin);
freopen("hs.out", "w", stdout);
po[0] = 1;
FOR(i, 1, 2000000) po[i] = po[i - 1] * BASE;
std::ios::sync_with_stdio(false);
cin >> t;
while (t--) {
cin >> n;
FOR(i, 1, n) cin >> str[i], len[i] = str[i].size();
bool isy = true, isn = true;
FOR(i, 1, n) {
gethash(i);
bool iss = false;
FOR(j, 0, len[i] - 1) {
if (str[i][j] == '*') isn = false, iss = true;
}
if (!iss) isy = false;
}
if (isn) {
bool flag = false;
FOR(i, 2, n) if (hs[i] != hs[i - 1]) {
flag = true;
break;
}
if (!flag) puts("Y");
else puts("N");
continue;
}
else if (isy) {
FOR(i, 1, n) {
FOR(j, 0, len[i] - 1) {
if (str[i][j] == '*') {
prefix[i] = j; break;
}
}
DNF(j, len[i] - 1, 0) {
if (str[i][j] == '*') {
suffix[i] = len[i] - 1 - j; break;
}
}
}
bool flag = true;
FOR(i, 1, n) numb[i] = i;
std::sort(numb + 1, numb + n + 1, cmp_prefix);
FOR(i, 2, n) {
int last = numb[i - 1], now = numb[i];
FOR(j, 1, prefix[last]) {
if (str[last][j - 1] != str[now][j - 1]) {
flag = false; puts("N"); break;
}
}
if (!flag) break;
}
if (!flag) continue;
std::sort(numb + 1, numb + n + 1, cmp_suffix);
FOR(i, 2, n) {
int last = numb[i - 1], now = numb[i];
FOR(j, 1, suffix[last]) {
if (str[last][len[last] - j] != str[now][len[now] - j]) {
flag = false; puts("N"); break;
}
}
if (!flag) break;
}
if (flag) puts("Y");
continue;
}
else {
work();
}
}
return 0;
}
Day2 T2 道路堵塞
这是一道玄学题,需要依赖splay的复杂度的不确定性进行解题。
所以我跳过了它。
Day2 T3 江南乐
思路
首先用SG函数就可以做到 O(n2) (枚举分开的情况,然后用朴素的统计就行了)
然后因为这题是分石子,我们可以发现分出来的石子很多都是一样的。
其实只可能分出两种石子,大小为 nmodi 的,和大小为 i−nmodi 的。
根据数量的奇偶性,最终会被消成不超过两个。
因为只关注奇偶性,所以对于
⌊n/i⌋
相等的多种分法,只需要计算最小的
i
和
复杂度为 O(nn√) 。
代码
#include <bits/stdc++.h>
typedef long long LL;
#define FOR(i, a, b) for (int i = (a), i##_END_ = (b); i <= i##_END_; i++)
#define DNF(i, a, b) for (int i = (a), i##_END_ = (b); i >= i##_END_; i--)
template <typename Tp> void in(Tp &x) {
char ch = getchar(), f = 1; x = 0;
while (ch != '-' && (ch < '0' || ch > '9')) ch = getchar();
if (ch == '-') f = -1, ch = getchar();
while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
x *= f;
}
template <typename Tp> Tp chkmax(Tp &x, Tp y) {return x > y ? x : x=y;}
template <typename Tp> Tp chkmin(Tp &x, Tp y) {return x < y ? x : x=y;}
template <typename Tp> Tp Max(Tp x, Tp y) {return x > y ? x : y;}
template <typename Tp> Tp Min(Tp x, Tp y) {return x < y ? x : y;}
const int MAXN = 100010;
int t, f, sg[MAXN], can[MAXN];
int main()
{
freopen("game.in", "r", stdin);
freopen("game.out", "w", stdout);
in(t); in(f);
FOR(i, 0, f - 1) sg[i] = 0;
FOR(i, f, 100000) {
for (int j = 2, k; j <= i; j = k + 1) {
k = i / (i / j);
int s1 = j, s2 = j + 1;
int a = ((i % s1) & 1) ? (i / s1 + 1) : 0;
int b = ((s1 - (i % s1)) & 1) ? (i / s1) : 0;
can[sg[a] ^ sg[b]] = i;
a = ((i % s2) & 1) ? (i / s2 + 1) : 0;
b = ((s2 - (i % s2)) & 1) ? (i / s2) : 0;
can[sg[a] ^ sg[b]] = i;
}
FOR(j, 0, 100000)
if (can[j] != i) {
sg[i] = j;
break;
}
}
FOR(i, 1, t) {
int n, ret = 0; in(n);
FOR(j, 1, n) {
int x; in(x); ret ^= sg[x];
}
if (ret) printf("1");
else printf("0");
if (i != t) printf(" ");
}
return 0;
}