以前写过一份 Day2 的。
这套题难度很大。当年赛场上貌似得分率不高。
寻宝游戏
把 (vee) 和 (land) 看成 0/1,与原序列对比发现变成答案中的 0/1 等价于比较数字的大小。最后排序即可。
#include <bits/stdc++.h>
using std::sort;
const int N = 1005, M = 5005, P = 1e9 + 7;
int n, m, Q, a[M][N], l[M]; char ch[M];
bool cmp(int x, int y) {
for (int i = n; i; i--)
if (a[x][i] != a[y][i]) return a[x][i] < a[y][i];
return 0;
}
int main() {
scanf("%d%d%d", &n, &m, &Q);
for (int i = 1; i <= n; i++) {
scanf("%s", ch+1);
for (int j = 1; j <= m; j++)
a[j][i] = ch[j]-'0';
}
for (int i = 1; i <= m; i++) l[i] = i;
sort(l+1, l+m+1, cmp);
while (Q--) {
int x = 0, y = 0, ok = 1;
scanf("%s", ch+1);
for (int i = 1; i <= m; i++) {
if (ch[l[i]] == '1' && !x) x = i;
if (ch[l[i]] == '0') y = i;
}
if (x) for (int i = n; i; i--)
if (a[l[x]][i] != a[l[y]][i]) {
if (a[l[x]][i] < a[l[y]][i]) ok = 0;
break;
}
if (!ok) puts("0");
else {
int ans = x?0:1;
for (int i = n; i; i--)
ans = ((2*ans+a[l[x]][i]-a[l[y]][i])%P + P)%P;
printf("%d
", ans);
}
}
return 0;
}
转盘
两处缩放:第一处在选取解时,我们把选取每个物品的时间在总时间不变的情况下尽可能推迟。发现当一个递增序列的值处处大于时间则递增序列最大值即为答案。
第二处在于发现上述问题的性质,破环成链再对下标差分后,求 每个长度为 (n) 的区间最大值+一个与下标相关的偏移量 前面这个整体的 最小值。分析发现可以进一步把区间长度放到后缀长度。这一点很重要。
后缀max递减,最后发现实际上就是一个楼房重建问题。复杂度 (mathcal O(nlog^2 n))。
#include <bits/stdc++.h>
using std::max;
using std::min;
const int N = 100005;
int n, m, p, las;
#define lc (o << 1)
#define rc (o << 1 | 1)
int ma[N*4], val[N*4], fix[N*4];
int query(int o, int l, int r, int v) {
if (l == r) return v < ma[o] ? val[o] : v+l;
int mid = l+r>>1;
return v > ma[rc] ? min(query(lc, l, mid, v), v+mid+1) : min(fix[o], query(rc, mid+1, r, v));
}
void pushup(int o, int l, int r) {
int mid = l+r>>1;
ma[o] = max(ma[lc], ma[rc]);
val[o] = min(val[rc], fix[o] = query(lc, l, mid, ma[rc]));
}
void modify(int o, int l, int r, int p, int v) {
if (l == r) { ma[o] = v; val[o] = v+p; return; }
int mid = l+r>>1;
p <= mid ? modify(lc, l, mid, p, v) : modify(rc, mid+1, r, p, v);
pushup(o, l, r);
}
int main() {
scanf("%d%d%d", &n, &m, &p);
for (int i = 1; i <= n; i++) { int x; scanf("%d", &x), modify(1, 1, n, i, x-i); }
printf("%d
", las = query(1, 1, n, ma[1]-n) + n - 1);
while (m--) {
int x, y; scanf("%d%d", &x, &y); p && (x ^= las, y ^= las);
modify(1, 1, n, x, y-x);
printf("%d
", las = query(1, 1, n, ma[1]-n) + n - 1);
}
return 0;
}
毒瘤
合理删去 (n-m+1) 条边后是一棵树。这样子做 DP 还需要关心去掉的 (n-m+1) 对限制。
枚举这些限制,总方案数为 (3 imes 2^{n-m+1}=mathcal O(2^{n-m+1}))。给上限制再做 DP 每次都要 (mathcal O(n)) 的复杂度,太屑了。
考虑优化,注意到 DP 实际上只跟这些点构成的虚树有关。然后就可以类似 NOIP2018 的 D2T3 一样搞,只不过这里不用倍增。仍然要注意维护 DP 值的顺序。
总复杂度 (mathcal O(n + s2^s)),这里 (s=n-m+1)。
#include <bits/stdc++.h>
using std::vector;
const int N = 100020, P = 998244353;
int n, m, ans = 0;
struct Edge {
int v, nxt;
} e[N*2];
int edges = 0, G[N];
void adde(int u, int v) {
e[edges++] = (Edge){v, G[u]}, G[u] = edges-1;
}
int fa[N];
int fset(int x) { return fa[x] == x ? x : fa[x] = fset(fa[x]); }
void merge(int x, int y) { fa[fset(x)] = fset(y); }
int tag[N], f[N][2], g[N][2][2];
vector<int> e2[N], l;
int dfs1(int u, int fa) {
f[u][0] = f[u][1] = 1;
for (int i = G[u], v; ~i; i = e[i].nxt) {
if (v = e[i].v, v == fa) continue;
int t = dfs1(v, u);
f[u][0] = 1LL * f[u][0] * (f[v][0] + f[v][1]) % P;
f[u][1] = 1LL * f[u][1] * f[v][0] % P;
if (t) e2[u].push_back(t);
}
if (!tag[u]) tag[u] = e2[u].size() > 1 ? u : e2[u].size() ? e2[u][0] : 0;
return tag[u];
}
void dfs2(int u, int fa) {
for (int i = G[u], v; ~i; i = e[i].nxt) {
if (v = e[i].v, v == fa) continue;
dfs2(v, u); int x = tag[v];
if (!x) continue;
g[x][0][0] = (g[x][0][0] + g[x][0][1]) % P, g[x][0][1] = (g[x][0][0] + P - g[x][0][1]) % P;
g[x][1][0] = (g[x][1][0] + g[x][1][1]) % P, g[x][1][1] = (g[x][1][0] + P - g[x][1][1]) % P;
}
g[u][0][0] = g[u][1][1] = 1;
for (int i = G[u], v; ~i; i = e[i].nxt) {
if (v = e[i].v, v == fa || tag[v]) continue;
int x = tag[u];
g[x][0][0] = 1LL * g[x][0][0] * (f[v][0] + f[v][1]) % P;
g[x][0][1] = 1LL * g[x][0][1] * f[v][0] % P;
g[x][1][0] = 1LL * g[x][1][0] * (f[v][0] + f[v][1]) % P;
g[x][1][1] = 1LL * g[x][1][1] * f[v][0] % P;
}
}
int dp[N][2], col[N];
void calc(int u) {
dp[u][0] = dp[u][1] = 1;
if (~col[u]) dp[u][col[u]^1] = 0;
for (int i = 0, v; i < e2[u].size(); i++) {
v = e2[u][i]; calc(v);
dp[u][0] = 1LL * dp[u][0] * ((1LL * dp[v][0] * g[v][0][0] + 1LL * dp[v][1] * g[v][1][0]) % P) % P;
dp[u][1] = 1LL * dp[u][1] * ((1LL * dp[v][0] * g[v][0][1] + 1LL * dp[v][1] * g[v][1][1]) % P) % P;
}
}
int main() {
scanf("%d%d", &n, &m);
memset(G, -1, sizeof G);
for (int i = 1; i <= n; i++) fa[i] = i;
for (int i = 1; i <= m; i++) {
int u, v; scanf("%d%d", &u, &v);
if (fset(u) == fset(v)) l.push_back(u), l.push_back(v), tag[u] = u, tag[v] = v;
else adde(u, v), adde(v, u), merge(u, v);
}
tag[1] = 1; dfs1(1, 0); dfs2(1, 0);
memset(col, -1, sizeof col);
for (int S = 0; S < 1<<(m-n+1)*2; S++) { // 这里不小心写成了2^2s的枚举,不过人没事
for (int i = 0; i < (m-n+1)*2; i++) col[l[i]] = -1;
int ok = 1;
for (int i = 0; i < (m-n+1)*2; i++) {
if (~col[l[i]] && col[l[i]] != (S >> i & 1)) { ok = 0; break; }
col[l[i]] = S >> i & 1;
}
for (int i = 0; i < (m-n+1)*2; i += 2)
if (col[l[i]] && col[l[i+1]]) { ok = 0; break; }
if (!ok) continue;
calc(1);
ans = (ans + 1LL * dp[1][0] * g[1][0][0] + 1LL * dp[1][1] * g[1][1][1]) % P;
}
printf("%d", ans);
return 0;
}