2013
D1T1 转圈游戏
Solution
(ans = x + 10 ^ km (mod n))
敲个快速幂就完事了。
Code
#include <bits/stdc++.h>
int x, n, m, k;
int main() {
std::cin >> n >> m >> k >> x; m %= n, x %= n;
int res = 1, p = 10;
while(k) {
if(k & 1) res = res * p % n;
p = p * p % n;
k >>= 1;
}
res = ((res * m) % n + x) % n;
std::cout << res;
return 0;
}
D1T2 火柴排队
Solution
显然两个序列的元素的大小关系一一对应时最小,题目中又规定了序列中元素两两不同使得简化了好多运算,两个数组全都离散化之后按照第一个数组对应元素的下标作为一个偏序关系对第二个数组求个逆序对就行了。
Code
#include <bits/stdc++.h>
const int Maxn = 1e5 + 5, Mod = 1e8 - 3;
template <typename Temp> inline void read(Temp & res) {
Temp fh = 1; res = 0; char ch = getchar();
for(; !isdigit(ch); ch = getchar()) if(ch == '-') fh = -1;
for(; isdigit(ch); ch = getchar()) res = (res << 3) + (res << 1) + (ch ^ '0');
res = res * fh;
}
int n, a[Maxn], b[Maxn], c[Maxn], d[Maxn], p[Maxn], k;
void add(int pos, int x) {while(pos <= n) d[pos] = (d[pos] + x) % Mod, pos += (-pos&pos);}
int query(int pos) {int res = 0; while(pos) res = (res + d[pos]) % Mod, pos -= (-pos&pos); return res;}
int main() {
read(n); int ans = 0;
for(int i = 1; i <= n; ++i) read(a[i]), p[i] = i;
std::sort(p + 1, p + n + 1, [](int x, int y) {return a[x] < a[y];});
for(int i = 1; i <= n; ++i) b[p[i]] = i;
for(int i = 1; i <= n; ++i) read(a[i]), p[i] = i;
std::sort(p + 1, p + n + 1, [](int x, int y) {return a[x] < a[y];});
for(int i = 1; i <= n; ++i) c[p[i]] = i;
for(int i = 1; i <= n; ++i) a[b[i]] = i;
for(int i = n; i >= 1; --i) {
ans = (ans + query(a[c[i]] - 1)) % Mod;
add(a[c[i]], 1);
}
printf("%d", ans);
return 0;
}
D2T2 花匠
Solution
?我怎么一场比赛打了俩树状数组(
很显然的东西吧……暴力用树状数组维护一下前缀最大值就行了……
Code
#include <bits/stdc++.h>
const int Maxn = 1e6 + 5, N = 1e6 + 2;
template <typename Temp> inline void read(Temp & res) {
Temp fh = 1; res = 0; char ch = getchar();
for(; !isdigit(ch); ch = getchar()) if(ch == '-') fh = -1;
for(; isdigit(ch); ch = getchar()) res = (res << 3) + (res << 1) + (ch ^ '0');
res = res * fh;
}
int n, h, mx = 0;
struct BIT {
int arr[Maxn];
void modify(int x, int d) {while(x <= N) arr[x] = std::max(arr[x], d), x += (-x&x);}
int query(int x) {if(x < 1) return 0; if(x > N) x = N; int res = 0; while(x) res = std::max(res, arr[x]), x -= (-x&x); return res;}
} a, b;
int main() {
read(n); if(n == 1) return printf("1"), 0;
while(n--) {
read(h);
int res1 = a.query(h) + 1; if(res1 > mx) mx = res1;
int res2 = b.query(N - h) + 1; if(res2 > mx) mx = res2;
a.modify(h + 1, res2);
b.modify(N - h + 1, res1);
}
printf("%d", mx);
return 0;
}
D2T3 华容道
Solution
乍一看没思路,然后瞟了一眼题解发现曾经看到过这种做法,只保留有用状态,找出有用状态之间的关系,跑最短路。
思路及其简单,调试过程及其艰难。
Code
下面是长达 (5 ext{Kb}) 的代码。
#include <bits/stdc++.h>
template <typename Temp> void read(Temp & res) {
Temp fh = 1; res = 0; char ch = getchar();
for(; !isdigit(ch); ch = getchar()) if(ch == '-') fh = -1;
for(; isdigit(ch); ch = getchar()) res = (res << 3) + (res << 1) + (ch ^ '0');
res = res * fh;
}
template <typename TP, typename... TPS> void read(TP & arg, TPS & ... args) {
read(arg); read(args...);
}
template <typename Temp> void ckmin(Temp & num, Temp comp) {if(comp < num) num = comp;}
const int Maxn = 30 * 30 * 4 + 100;
int n, m, _d[35][35], q, ex, ey, sx, sy, tx, ty;
int dis0[35][35], cd[35][35]; bool inq[Maxn];
bool _(int x, int y) {
return 1 <= x && x <= n && 1 <= y && y <= m && _d[x][y];
}
int s(int x, int y, int kind) {//state
//printf("(%d %d %d) - ", x, y, kind);
int nd = (x - 1) * m + y;
return (nd - 1) * 4 + kind;
}
struct edge {int ver, len; edge() {} edge(int A, int B){ver = A, len = B;}};
std::vector<edge> g[Maxn]; int dis[Maxn];
void add(int u, int v, int w) {g[u].push_back(edge(v, w)); /*printf(" %d - added
", w);*/}
const int px[] = {-1, 0, 1, 0}, py[] = {0, -1, 0, 1};
void bfsfordist(int wx, int wy, int k) {
//printf("bfs for (%d, %d)
", wx + px[k], wy + py[k]);
memset(dis0, 0x3f, sizeof dis0);
auto _0 = [wx, wy](int x, int y) {
return 1 <= x && x <= n && 1 <= y && y <= m && _d[x][y] && (!(x == wx && y == wy));
};
std::queue<int> qx, qy; dis0[wx + px[k]][wy + py[k]] = 0; qx.push(wx + px[k]), qy.push(wy + py[k]);
while(!qx.empty()) {
int ux = qx.front(), uy = qy.front(); qx.pop(), qy.pop();
for(int k = 0; k < 4; ++k)
if(_0(ux + px[k], uy + py[k]))
if(dis0[ux + px[k]][uy + py[k]] > dis0[ux][uy] + 1) {
dis0[ux + px[k]][uy + py[k]] = dis0[ux][uy] + 1;
qx.push(ux + px[k]), qy.push(uy + py[k]);
}
}
/*
for(int i = 1; i <= n; ++i) {
for(int j = 1; j <= m; ++j) printf("%-3d ", dis0[i][j] > 1000000 ? -1 : dis0[i][j]);
puts("");
}
system("pause");*/
}
void prework() {
//memset(dis0, 0x3f, sizeof dis0);
//printf("%d %d %d %d %d %d
", ex, ey, sx, sy, tx, ty);
/*
for(int i = 1; i <= n; ++i) {
for(int j = 1; j <= m; ++j) printf("%-3d ", dis0[i][j] > 1000000 ? -1 : dis0[i][j]);
puts("");
}*/
/*
for(int i = 0; i <= n * m * 4; ++i) {
printf("node: %d
", i);
for(auto e : g[i]) printf("(%d,%d)", e.ver, e.len); puts("");
}*/
}
int work() {
g[0].clear();
auto _0 = [](int x, int y) {
return 1 <= x && x <= n && 1 <= y && y <= m && _d[x][y] && (!(x == sx && y == sy));
};
memset(dis, 0x3f, sizeof dis); memset(dis0, 0x3f, sizeof dis0);
//for(int u = 0; u <= n * m * 4; ++u) g[u].clear();
read(ex, ey, sx, sy, tx, ty);
if(sx == tx && sy == ty) return printf("0
"), 0;
std::queue<int> qx, qy; dis0[ex][ey] = 0; qx.push(ex), qy.push(ey);
while(!qx.empty()) {
int ux = qx.front(), uy = qy.front(); qx.pop(), qy.pop();
for(int k = 0; k < 4; ++k)
if(_0(ux + px[k], uy + py[k]))
if(dis0[ux + px[k]][uy + py[k]] > dis0[ux][uy] + 1) {
dis0[ux + px[k]][uy + py[k]] = dis0[ux][uy] + 1;
qx.push(ux + px[k]), qy.push(uy + py[k]);
}
}
for(int k = 0; k < 4; ++k) {
if(!_(sx + px[k], sy + py[k])) continue;
if(dis0[sx + px[k]][sy + py[k]] ^ dis0[0][0]) add(0, s(sx, sy, k + 1), dis0[sx + px[k]][sy + py[k]]);
}
dis[0] = 0; std::queue<int> q; q.push(0); inq[0] = 1;
while(!q.empty()) {
int u = q.front(); q.pop(); inq[u] = 0;
//std::cerr << u << ' ' << dis[u] << '
';
for(auto e : g[u]) {
if(dis[u] + e.len < dis[e.ver]) {
//std::cerr << u << ' ' << dis[u] + e.len << ' ' << e.ver << ' ' << dis[e.ver] << '
';
dis[e.ver] = dis[u] + e.len;
if(!inq[e.ver]) {
inq[e.ver] = 1;
q.push(e.ver);
}
}
}
}
int res = 0x7fffffff;
for(int k = 1; k <= 4; ++k) ckmin(res, dis[s(tx, ty, k)]);
if(res > 1000000) return printf("-1
"), 0;
else printf("%d
", res);
return 0;
}
int main() {
//freopen("hrd.txt", "w", stdout);
read(n, m, q);
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= m; ++j)
read(_d[i][j]);
for(int i = 1; i <= n; ++i) {
for(int j = 1; j <= m; ++j) {
if(!_(i, j)) continue;
for(int k = 0; k < 4; ++k) {
if(_(i + px[k], j + py[k])) {
bfsfordist(i, j, k);
for(int l = 0; l < 4; ++l) {
if(l == k || !_(i + px[l], j + py[l])) continue;
if(dis0[i + px[l]][j + py[l]] <= 10000) add(s(i, j, k + 1), s(i, j, l + 1), dis0[i + px[l]][j + py[l]]);
}
}
}
}
}
for(int i = 1; i < n; ++i) {
for(int j = 1; j <= m; ++j) {
if(_(i, j) && _(i + 1, j)) add(s(i, j, 3), s(i + 1, j, 1), 1), add(s(i + 1, j, 1), s(i, j, 3), 1);
}
}
for(int i = 1; i <= n; ++i) {
for(int j = 1; j < m; ++j) {
if(_(i, j) && _(i, j + 1)) add(s(i, j, 4), s(i, j + 1, 2), 1), add(s(i, j + 1, 2), s(i, j, 4), 1);
}
}
/*
printf("%d
", s(1, 2, 3));
for(auto e : g[s(1, 2, 3)]) printf("%d ", e.ver); puts("");
*/
while(q--) work();
return 0;
}
2015
D2T2 子串
Solution
一些约定:
- 若 (S) 为字符串,则用 (|S|) 表示字符串的长度,(S[l:r]) 表示从 (l) 到 (r) 的一个连续子串,特别的,当 (l = 1) 时表示 (S) 的一个前缀,(r = |S|) 时表示后缀。
- ([A]) 中 ([]) 为艾佛森括号,其中 (A) 为逻辑命题,当 (A) 为真时 ([A]) 为 (1),为假时表达式的值为 (0)。
下文 (k) 并不代表原来的 (k) 的含义,而是循环变量。
设 (f(i, j, k)) 表示在 (A[1 : i]) 中选出 (k) 段使其与 (B[1 : j]) 相等的划分方案数。
显然有 (displaystyle f(i, j, k) = f(i - 1, j, k) + sum_{A[i - x + 1 : i] = B[j - x + 1 : j]}f(i - x, j - x, k - 1))。
边界条件 (f(i, j, 0) = [j = 0])。
复杂度 (O(nm^2k)),按照这个方式编写即可得到 (70) 分。(理论 MLE + TLE,实际 MLE。)
(代码见下文 Code 70 pts。)
注意到在求解过程中 (k) 那一维只取到了 (k) 和 (k - 1),因此可以优先枚举这一维,然后滚动数组优化空间。(理论 TLE,实际 AC。)
(代码见下文 Code 100 pts(1)。)
再次注意到,如果 (A[l_1: i] eq B[l_2, j]),那么 (A[l_1 - 1: i]) 一定也不等于 (B[l_2 - 1: j]),转移时后面的求和部分决策的左端点单调不降,因此可以再开一个辅助数组 (g(i, j)) 记录一下 (displaystylesum_{A[i - x + 1 : i] = B[j - x + 1 : j]}f(i - x, j - x, k - 1)),如果 (A_i = B_j),则 (g(i, j) = g(i - 1, j - 1) + f(i - 1, j - 1, k - 1)),否则令 (g(i, j) = 0),复杂度 (O(nmk)),可以通过本题。
(代码见下文 Code 100 pts(2)。)
Code 70 pts
#include <cstdio>
#include <algorithm>
template <typename Temp>
void read(Temp & res) {
auto isdigit = [](char ch){return '0' <= ch && ch <= '9';};
Temp fh = 1; res = 0; char ch = getchar();
for(; !isdigit(ch); ch = getchar()) if(ch == '-') fh = -1;
for(; isdigit(ch); ch = getchar()) res = (res << 3) + (res << 1) + (ch ^ '0');
res = res * fh;
}
template <typename tp, typename ... tps>
void read(tp & arg, tps & ... args) {
read(arg); read(args ...);
}
int n, m, k; char a[1005], b[205]; int f[1005][205][205];
int main() {
read(n, m, k); scanf("%s%s", a + 1, b + 1); f[0][0][0] = 1;
for(int i = 1; i <= n; ++i) {
for(int j = 0, M = std::min(m, i); j <= M; ++j) {
f[i][j][0] = (j == 0);
for(int l = 1, K = std::min(k, j); l <= K; ++l) {
f[i][j][l] = f[i - 1][j][l];
int x;
for(x = 1; j - x >= 0; ++x) {
if(a[i - x + 1] != b[j - x + 1]) break;
f[i][j][l] = (f[i][j][l] + f[i - x][j - x][l - 1]) % 1000000007;
}
//printf("f(%d, %d, %d): %d, trans point: %d
", i, j, l, f[i][j][l], x - 1);
}
}
}
printf("%d", f[n][m][k]);
return 0;
}
Code 100 pts(1)
#include <cstdio>
#include <algorithm>
#include <cstring>
//#include <iostream>
template <typename Temp>
void read(Temp & res) {
auto isdigit = [](char ch){return '0' <= ch && ch <= '9';};
Temp fh = 1; res = 0; char ch = getchar();
for(; !isdigit(ch); ch = getchar()) if(ch == '-') fh = -1;
for(; isdigit(ch); ch = getchar()) res = (res << 3) + (res << 1) + (ch ^ '0');
res = res * fh;
}
template <typename tp, typename ... tps>
void read(tp & arg, tps & ... args) {
read(arg); read(args ...);
}
int n, m, k; char a[1005], b[205]; int f[2][1005][205];
int main() {
read(n, m, k); scanf("%s%s", a + 1, b + 1); f[0][0][0] = 1;
for(int i = 1; i <= n; ++i) for(int j = 0, M = std::min(m, i); j <= M; ++j) f[0][i][j] = (j == 0);
//std::cerr << 'f' << '
';
for(int l = 1, p = 1; l <= k; ++l, p ^= 1) {
memset(f[p], 0, sizeof f[p]);
for(int i = l; i <= n; ++i) {
for(int j = l, M = std::min(m, i); j <= M; ++j) {
//for(int l = 1, K = std::min(k, j); l <= K; ++l) {
f[p][i][j] = f[p][i - 1][j];
for(int x = 1; j - x >= 0; ++x) {
if(a[i - x + 1] != b[j - x + 1]) break;
f[p][i][j] = (f[p][i][j] + f[p ^ 1][i - x][j - x]) % 1000000007;
}
//printf("f(%d, %d, %d): %d
", l, i, j, f[p][i][j]);
//}
}
}
}
printf("%d", f[k & 1][n][m]);
return 0;
}
Code 100 pts(2)
#include <cstdio>
#include <cstring>
template <typename Temp>
void read(Temp & res) {
auto isdigit = [](char ch){return '0' <= ch && ch <= '9';};
Temp fh = 1; res = 0; char ch = getchar();
for(; !isdigit(ch); ch = getchar()) if(ch == '-') fh = -1;
for(; isdigit(ch); ch = getchar()) res = (res << 3) + (res << 1) + (ch ^ '0');
res = res * fh;
}
template <typename tp, typename ... tps>
void read(tp & arg, tps & ... args) {
read(arg); read(args ...);
}
int n, m, k; char a[1005], b[205]; int f[2][1005][205];
int g[1005][205];
int main() {
read(n, m, k); scanf("%s%s", a + 1, b + 1); f[0][0][0] = 1; auto min = [](int A, int B) {return A < B ? A : B;};
for(int i = 1; i <= n; ++i) for(int j = 0, M = min(m, i); j <= M; ++j) f[0][i][j] = (j == 0);
for(int l = 1, p = 1; l <= k; ++l, p ^= 1) {
memset(f[p], 0, sizeof f[p]);
memset(g, 0, sizeof g);
for(int i = l; i <= n; ++i) {
for(int j = l, M = min(m, i); j <= M; ++j) {
f[p][i][j] = f[p][i - 1][j];
if(a[i] != b[j]) g[i][j] = 0;
else g[i][j] = (g[i - 1][j - 1] + f[p ^ 1][i - 1][j - 1]) % 1000000007,
f[p][i][j] = (f[p][i][j] + g[i][j]) % 1000000007;
/*for(int x = 1; j - x >= 0; ++x) {
if(a[i - x + 1] != b[j - x + 1]) break;
f[p][i][j] = (f[p][i][j] + f[p ^ 1][i - x][j - x]) % 1000000007;
}*/
}
}
}
printf("%d", f[k & 1][n][m]);
return 0;
}
2016
D1T2 天天爱跑步
Solution
第三次尝试莽这道题了,这次做的倒还算顺利。读完题,想到最无脑的做法:直接线段树合并。
把一条路径拆成上行和下行,上行过程中深度加时间是定值,下行过程中深度减时间是定值,两遍分开维护。
Code
#include <bits/stdc++.h>
template <typename Temp> inline void read(Temp & res) {
Temp fh = 1; res = 0; char ch = getchar();
for(; !isdigit(ch); ch = getchar()) if(ch == '-') fh = -1;
for(; isdigit(ch); ch = getchar()) res = (res << 3) + (res << 1) + (ch ^ '0');
res = res * fh;
}
const int Maxn = 3e5 + 5;
int n, m, x, y, ans[Maxn], qt[Maxn], s[Maxn], t[Maxn], dep[Maxn];
int tr[Maxn * 81], ls[Maxn * 81], rs[Maxn * 81], cnt;
void modify(int pos, int d, int l, int r, int u) {
if(l == r) {tr[u] += d; return;} int mid = (l + r) >> 1;
if(pos <= mid) modify(pos, d, l, mid, ls[u] ? ls[u] : ls[u] = ++cnt);
else modify(pos, d, mid + 1, r, rs[u] ? rs[u] : rs[u] = ++cnt);
tr[u] = tr[ls[u]] + tr[rs[u]];
}
int query(int pos, int l, int r, int u) {
if(l == r) return tr[u]; int mid = (l + r) >> 1;
if(pos <= mid) return query(pos, l, mid, ls[u]);
else return query(pos, mid + 1, r, rs[u]);
}
int merge(int u, int v, int l, int r) {
if(!u) return v; if(!v) return u;
if(l == r) return tr[u] += tr[v], u;
int mid = (l + r) >> 1;
ls[u] = merge(ls[u], ls[v], l, mid);
rs[u] = merge(rs[u], rs[v], mid + 1, r);
return tr[u] = tr[ls[u]] + tr[rs[u]], u;
}
int ver[Maxn << 1], nxt[Maxn << 1], hd[Maxn], ecnt;
void add(int u, int v) {ecnt++; ver[ecnt] = v; nxt[ecnt] = hd[u]; hd[u] = ecnt;}
int fa[Maxn], top[Maxn], size[Maxn], h[Maxn];
void dfs(int u, int f) {
size[u] = 1; fa[u] = f; dep[u] = dep[f] + 1;
for(int i = hd[u]; i; i = nxt[i]) {
int v = ver[i]; if(v == f) continue;
dfs(v, u); size[u] += size[v];
if(size[v] > size[h[u]]) h[u] = v;
}
}
int lca(int u, int v) {
while(top[u] ^ top[v]) {
if(dep[top[u]] < dep[top[v]]) v = fa[top[v]];
else u = fa[top[u]];
}
if(dep[u] < dep[v]) return u; else return v;
}
int p[Maxn]; bool cmp(int a, int b) {return dep[a] < dep[b];}
#define dbg for(int i = 1; i <= n; ++i) printf("%d ", ans[i]); puts("");
int main() {
read(n); read(m); cnt = n;
for(int i = 1; i < n; ++i) read(x), read(y), add(x, y), add(y, x); dfs(1, 0);
for(int i = 1; i <= n; ++i) read(qt[i]), p[i] = i; std::sort(p + 1, p + n + 1, cmp);
for(int i = 1; i <= n; ++i) {if(!top[p[i]]) top[p[i]] = p[i]; if(h[p[i]]) top[h[p[i]]] = top[p[i]];}
for(int i = 1; i <= m; ++i) {
read(s[i]); read(t[i]);
int z = lca(s[i], t[i]);
modify(dep[s[i]], 1, 1, n, s[i]);
if(fa[z]) modify(dep[s[i]], -1, 1, n, fa[z]);
}
for(int i = n; i >= 1; --i) {
int u = p[i];
if(dep[u] + qt[u] <= n) ans[u] = query(dep[u] + qt[u], 1, n, u);
if(fa[u]) merge(fa[u], u, 1, n);
}
memset(tr, 0, sizeof tr); memset(ls, 0, sizeof ls); memset(rs, 0, sizeof rs); cnt = n;
for(int i = 1; i <= m; ++i) {
int z = lca(s[i], t[i]), l = dep[s[i]] + dep[t[i]] - 2 * dep[z];
modify(dep[t[i]] - l + n, 1, 1, n << 1, t[i]);
modify(dep[t[i]] - l + n, -1, 1, n << 1, z);
}
for(int i = n; i >= 1; --i) {
int u = p[i];
ans[u] += query(dep[u] - qt[u] + n, 1, n << 1, u);
if(fa[u]) merge(fa[u], u, 1, n << 1);
}
for(int i = 1; i <= n; ++i) printf("%d ", ans[i]);
return 0;
}
D2T2 蚯蚓
Solution
注意到 (O((n + m)log(n + m))) 的做法非常 nt,除了这两条蚯蚓之外其余蚯蚓增加长度的过程,就相当于全局增加固定长度然后这两条长度减少。
在此基础上发现如果被切蚯蚓的长度是有序的,产生的两条蚯蚓的长度也是对应有序的,这样就能做到 (O(n + m)) 了。
不定长度参数列表和 Lambda 表达式真是太好用啦!
Code
#include <cstdio>
#include <algorithm>
#define LL long long
template <typename Temp>
void read(Temp & res) {
auto isdigit = [](char ch){return '0' <= ch && ch <= '9';};
Temp fh = 1; res = 0; char ch = getchar();
for(; !isdigit(ch); ch = getchar()) if(ch == '-') fh = -1;
for(; isdigit(ch); ch = getchar()) res = (res << 3) + (res << 1) + (ch ^ '0');
res = res * fh;
}
template <typename tp, typename ... tps>
void read(tp & arg, tps & ... args) {
read(arg); read(args ...);
}
LL q[3][8000005]; int h[3], t[3]; LL a[100005];
int n, m, p; LL l, u, v;
int main() {
read(n, m, l, u, v, p); h[0] = h[1] = h[2] = 1;
for(int i = 1; i <= n; ++i) read(a[i]);
std::sort(a + 1, a + n + 1);
for(int i = n; i >= 1; --i) q[0][++t[0]] = a[i];
auto _ = [](){
LL __[3]; int ____ = 0;
for(int ___ = 0; ___ < 3; ++___) {
if(t[___] < h[___]) __[___] = ~0x7fffffffffffffff;
else __[___] = q[___][h[___]];
if(__[___] > __[____]) ____ = ___;
}
return ____;
};
for(int i = 1; i <= m; ++i) {
/*
for(int k = 0; k < 3; ++k) {
printf("%d - th queue: ", k);
for(int j = h[k]; j <= t[k]; ++j) printf("%lld ", q[k][j]);
puts("");
}
printf("choose: %d
", _());
*/
int d = _(); LL len = q[d][h[d]++] + l * (i - 1);
if(i % p == 0) printf("%lld ", len);
LL len1 = len * u / v, len2 = len - len1;
q[1][++t[1]] = len1 - l * i, q[2][++t[2]] = len2 - l * i;
}
printf("
");
for(int i = 1; i <= n + m; ++i) {
int d = _(); if(i % p == 0) printf("%lld ", q[d][h[d]++] + l * m); else h[d]++;
}
return 0;
}
D2T3 愤怒的小鸟
Solution
注意到 (O(n^3 + n^22^n)) 的做法非常 nt,直接两点加坐标原点确定 (O(n^2)) 条抛物线,数学方法确定系数,然后判断有多少头猪在这上头即可。
由于对轨迹的限制比较多,大致带着一个 (dfrac{1}{8}) 的常数,因此 (O(n^3 + n^22^n)) 做法已经可以通过。
听说有严格 (O(n2^n)) 的做法,以后有机会再研究。
Code
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#define LF double
const LF eps = 1e-6;
int T, n, f[1 << 18], mask[189], cnt;
LF x[18], y[18];
int init() {scanf("%d", &T); return 0;}
int m = init();
int main() {
memset(f, 0x3f, sizeof f); cnt = f[0] = 0; scanf("%d%d", &n, &m);
for(int i = 0; i < n; ++i) scanf("%lf%lf", &x[i], &y[i]);
if(n == 1) {
printf("1
");
if(--T) main();
return 0;
}
for(int i = 0; i < n; ++i)
for(int j = i + 1; j < n; ++j) {
if(fabs(x[i] - x[j]) < eps) continue;
LF a = (y[i] * x[j] - y[j] * x[i]) / (x[i] * x[i] * x[j] - x[j] * x[j] * x[i]),
b = (y[i] * x[j] * x[j] - y[j] * x[i] * x[i]) / (x[j] * x[j] * x[i] - x[i] * x[i] * x[j]);
if(a >= -eps) continue;
mask[cnt] = 0;
for(int k = 0; k < n; ++k)
if(fabs(a * x[k] * x[k] + b * x[k] - y[k]) <= eps) mask[cnt] |= (1 << k);
//printf("%d th mask %d
", cnt, mask[cnt]);
cnt++;
}
for(int i = 0; i < n; ++i) mask[cnt++] = (1 << i);
for(int S = 0; S < (1 << n); ++S)
for(int i = 0; i < cnt; ++i)
f[S | mask[i]] = std::min(f[S | mask[i]], f[S] + 1);
printf("%d
", f[(1 << n) - 1]);
if(--T) main();
return 0;
}
2017
D2T2 宝藏
Solution
瞎写一发状压竟然过了
设 (f(i, S)) 表示当前最深深度为 (i),已经连通的节点集合为 (S) 时的最小代价,转移可以直接刷表,(f(i + 1, S cup T) larr f(i, S) + cost(S, T), T subseteq complement_US),其中 (cost(S, T)) 是预处理出来的将 (T) 中点与 (S) 中点连接的最小距离和。
比较迷惑的一点是,这样转移真的是不重不漏的吗?确实是的。
时间复杂度瓶颈在预处理上,为 (O(n^23^n)),dp 的复杂度反而为 (O(n3^n))。空间复杂度因为我直接存了一个不考虑子集的 (cost) 数组是 (O(4^n)) 的。
Code
#include <cstdio>
#include <cstring>
template <typename Temp>
void read(Temp & res) {
auto isdigit = [](char ch){return '0' <= ch && ch <= '9';};
Temp fh = 1; res = 0; char ch = getchar();
for(; !isdigit(ch); ch = getchar()) if(ch == '-') fh = -1;
for(; isdigit(ch); ch = getchar()) res = (res << 3) + (res << 1) + (ch ^ '0');
res = res * fh;
}
template <typename tp, typename ... tps>
void read(tp & arg, tps & ... args) {
read(arg); read(args ...);
}
int n, m, x, y; long long g[12][12], c[1 << 12][1 << 12], f[12][1 << 12], z;
int Main() {
auto ckmin = [](int &a, int b) {if(b < a) a = b;};
auto ckminll = [](long long &a, long long b) {if(b < a) a = b;};
read(n, m); memset(g, 63, sizeof g); memset(f, 63, sizeof f);
while(m--) read(x, y, z), x--, y--, ckminll(g[x][y], z), ckminll(g[y][x], z);
for(int S = 0; S < (1 << n); ++S) {
int P = (~S) & ((1 << n) - 1);
for(int T = P; T; T = (T - 1) & P) {
for(int i = 0; i < n; ++i) {
if(!(T & (1 << i))) continue;
int mn = 0x7fffffff;
for(int j = 0; j < n; ++j) {
if(!(S & (1 << j))) continue;
ckmin(mn, g[i][j]);
}
if(mn == 0x7fffffff) {
c[S][T] = 0x7fffffff;
break;
}
c[S][T] += mn;
}
}
}
for(int i = 0; i < n; ++i) f[0][(1 << i)] = 0;
for(int h = 0; h < n - 1; ++h) {
for(int S = 0; S < (1 << n); ++S) {
int P = (~S) & ((1 << n) - 1);
for(int T = P; T; T = (T - 1) & P) {
ckminll(f[h + 1][S | T], f[h][S] + 1ll * (h + 1) * c[S][T]);
}
}
}
long long mn = 0x7fffffff;
for(int h = 0; h < n; ++h) ckminll(mn, f[h][(1 << n) - 1]);
printf("%lld", mn);
return 0;
}
int mian = Main();
int main() {}
D2T3 列队
Solution
区间平移若干位是平衡树基础操作,只考虑相对顺序,把被移到后面的节点拿出来扔后头就行了。
然后对于不在最后一排的询问,直接用树状数组把它们离线处理下来。
具体操作就是初始全 (0),每次把被删除的位置 (1),然后在树状数组上倍增找 (k-th)。
平衡树码量巨大,一定要注意细节处理。
Code
#include <cstdio>
#include <vector>
#include <algorithm>
#define LL long long
#define lp puts("F**KCCF")
template <typename Temp>
void read(Temp & res) {
auto isdigit = [](char ch){return '0' <= ch && ch <= '9';};
Temp fh = 1; res = 0; char ch = getchar();
for(; !isdigit(ch); ch = getchar()) if(ch == '-') fh = -1;
for(; isdigit(ch); ch = getchar()) res = (res << 3) + (res << 1) + (ch ^ '0');
res = res * fh;
}
template <typename tp, typename ... tps>
void read(tp & arg, tps & ... args) {
read(arg); read(args ...);
}
const int Maxn = 3e5 + 5;
int n, m, q;
int qx[Maxn], qy[Maxn]; LL idx[Maxn];
std::vector<int> qs[Maxn];
struct BIT {
int array[Maxn], tim[Maxn], Tim, deleted = 0;
BIT() {Tim = 0;};
void del(int pos) {
if(tim[pos] ^ Tim) tim[pos] = Tim, array[pos] = 0;
while(pos <= m - 1) {
++array[pos];
pos += (-pos & pos);
}
++deleted;
}
int kth(int k) {
if(k > m - 1 - deleted) return 0;
int cur = 0, sum = 0;
for(int b = 20; ~b; --b) {
int to = cur + (1 << b);
if(to > m - 1) continue;
if(tim[to] ^ Tim) tim[to] = Tim, array[to] = 0;
int tsum = sum + array[to];
if(to - tsum < k) cur = to, sum = tsum;
}
return cur + 1;
}
void clear() {++Tim; deleted = 0;}
} bit;
struct splay_forest {
int ch[Maxn * 3][2], fa[Maxn * 3], sum[Maxn * 3], cnt_node, cnt[Maxn]; LL val[Maxn * 3];
void init(int N) {
cnt_node = N;
}
int get(int u) {
return ch[fa[u]][1] == u;
}
void maintain(int u) {
sum[u] = sum[ch[u][0]] + sum[ch[u][1]] + 1;
}
void rotate(int u) {
int f1 = fa[u], f2 = fa[f1], k1 = get(u), k2 = get(f1), v = ch[u][k1 ^ 1];
fa[u] = f2, ch[f2][k2] = u, ch[u][k1 ^ 1] = f1, fa[f1] = u, ch[f1][k1] = v, fa[v] = f1;
maintain(f1), maintain(u);
}
void splay(int u, int goal) {
int f1 = fa[u], f2 = fa[f1], k1 = get(u), k2 = get(f1);
while(f1 ^ goal) {
if(f2 == goal) rotate(u);
else {
if(k1 ^ k2) rotate(u), rotate(u);
else rotate(f1), rotate(u);
}
f1 = fa[u], f2 = fa[f1], k1 = get(u), k2 = get(f1);
}
}
int kth(int id, int k) {
int u = ch[id][1];
while(u) {
if(sum[ch[u][0]] < k && k <= sum[ch[u][0]] + 1) return u;
else if(k <= sum[ch[u][0]]) u = ch[u][0];
else k -= sum[ch[u][0]] + 1, u = ch[u][1];
}
return 0;
}
void Insert(int u, LL data, int id) {
if(!cnt[id]) {
++cnt[id];
ch[id][1] = u;
val[u] = data;
fa[u] = id;
sum[u] = 1;
ch[u][0] = ch[u][1] = 0;
return;
} else {
int res = kth(id, cnt[id]);
splay(res, id);
++cnt[id];
ch[res][1] = u;
val[u] = data;
fa[u] = res;
sum[u] = 1;
ch[u][0] = ch[u][1] = 0;
maintain(res);
splay(u, id);
}
}
void Delete(int u, int id) {
splay(u, id); --sum[u];
while(ch[u][0]) rotate(ch[u][0]);
int k = get(u), f = fa[u], v = ch[u][1];
ch[f][k] = v; if(v > n) fa[v] = f;
--cnt[id]; if(v > n) splay(v, id);
}
void print(int u) {
if(!u) return;
if(ch[u][0]) print(ch[u][0]);
printf("(%d, %lld, ls: %d, rs: %d, fa: %d, sum: %d)
", u, val[u], ch[u][0], ch[u][1], fa[u], sum[u]);
if(ch[u][1]) print(ch[u][1]);
}
} tr;
void Print() {
puts("");
for(int i = 0; i <= n; ++i) tr.print(tr.ch[i][1]); puts("");
puts("");
}
int main() {
read(n, m, q);
for(int i = 1; i <= q; ++i) read(qx[i]), read(qy[i]), qs[qx[i]].push_back(i);
for(int i = 1; i <= n; ++i) {
bit.clear();
int rev = m - 1;
for(int e : qs[i]) {
if(qy[e] == m) continue;
if(qy[e] > rev) {
qy[e] -= rev;
continue;
}
int res = bit.kth(qy[e]);
idx[e] = 1ll * (i - 1) * m + res;
rev--; bit.del(res);
}
}
tr.init(n);
for(int i = 1; i <= n; ++i) tr.Insert(++tr.cnt_node, 1ll * i * m, 0);
for(int i = 1; i <= q; ++i) {
if(idx[i]) {
printf("%lld
", idx[i]);
int u = tr.kth(0, qx[i]); LL v = tr.val[u];
tr.Delete(u, 0), tr.Insert(u, v, qx[i]);
tr.Insert(++tr.cnt_node, idx[i], 0);
} else {
if(qy[i] == m) {
int u = tr.kth(0, qx[i]); LL v = tr.val[u];
printf("%lld
", v);
tr.Delete(u, 0); tr.Insert(u, v, 0);
} else {
int u = tr.kth(qx[i], qy[i]); LL v = tr.val[u];
printf("%lld
", v);
tr.Delete(u, qx[i]), tr.Insert(u, v, 0);
u = tr.kth(0, qx[i]); v = tr.val[u];
tr.Delete(u, 0), tr.Insert(u, v, qx[i]);
}
}
//Print();
}
return 0;
}
/*
void check() {
tr.init(5);
tr.Insert(6, 2, 1);
tr.Insert(8, 3, 1);
tr.Insert(10, 1, 1);
tr.Insert(9, 0, 0);
tr.Insert(14, 19, 0);
tr.Insert(128, 3, 5);
for(int i = 0; i <= 5; ++i) tr.print(tr.ch[i][1]), puts("");
tr.Delete(9, 0);
tr.Insert(9, 213, 1);
tr.Insert(23133, 32145234, 0);
tr.Insert(34243, 324344, 0);
tr.Insert(27777, 222, 3);
tr.Delete(27777, 3);
tr.Insert(27777, 223, 3);
for(int i = 0; i <= 5; ++i) tr.print(tr.ch[i][1]), puts("");
}*/
2020
CSP-S T1 儒略日
Solution
有一个基于数学的代码量极小的做法。
Code
#include <bits/stdc++.h>
template <typename Temp> void read(Temp & res) {
Temp fh = 1; res = 0; char ch = getchar();
for(; !isdigit(ch); ch = getchar()) if(ch == '-') fh = -1;
for(; isdigit(ch); ch = getchar()) res = (res << 3) + (res << 1) + (ch ^ '0');
res = res * fh;
}
template <typename TP, typename... TPS> void read(TP & arg, TPS & ... args) {
read(arg); read(args...);
}
long long n, y, m, d;
void Main() {
read(n);
if (n > 2299160) n += 1 + (4 * n - 7322768) / 146097 * 3 / 4;
n += 1524,
y = (4 * n - 489) / 1461,
n -= y * 1461 / 4,
m = (5 * n - 1) / 153,
d = n - m * 153 / 5,
y -= 4716;
if (--m > 12) m -= 12, ++y;
if (y <= 0) printf("%lld %lld %lld BC
", d, m, 1 - y);
else printf("%lld %lld %lld
", d, m, y);
}
int main() {
int T; read(T); while(T--) Main();
return 0;
}
NOIP T4 微信步数
Solution
先确定开始的点,再按照题意的运动方式模拟的做法没有很大的优化空间。
果断改变思路,考虑让所有的点一起走,然后把走出了边界的点舍弃,可以证明这样行走在任何时刻剩余的点一定填满了一个棱平行于坐标轴的 (k) 维超立方体。
可以归纳证明:一开始所有的点显然填满了这个 (k) 维超立方体,之后每一步对它的影响都是至多从某个维度超越了边界,然后顺着这个 (k - 1) 维边界被削掉一层,剩余部分显然也是个 (k) 维超立方体。(可以考虑用 (3) 维的理解一下。)
这样对于每一步有贡献的点,就是走这一步之前,剩余的点构成的超立方体的 (k) 维体积。
直接按照这个方式模拟,判一下无解(走完 (n) 步之后总位移为 (0) 且仍旧存在剩余的点)即可得到 45 分。(代码可以见下面 Code 部分。)
正解做法非常优美。
因为剩余点是个棱平行于坐标轴的超立方体,那么在每个维度上一定排布成了一个连续区间。
手玩或者模拟可以发现一个关键的结论:除了前 (n) 步之外,剩下的每 (n) 步在每个维度上对剩余区间大小的减少量都相等。
这样可以先做一个预处理,先模拟一轮,求出每个维度上剩余区间的大小,每个维度之后每一轮的减少量,一轮中走第 (i) 步((1 le i le n))之前每个维度的剩余区间大小分别减小了多少。我将其分别记为 (rev(d), del(d)) 和 (f(i, d))。
然后求出这个周期性计算最多能支持多少完整的轮,即最多多少轮之后任意一个维度上剩余区间的大小仍严格大于 (0),记为 (t = minleft{dfrac{rev(i) - 1}{del(i)} ight})。多出来的那个不完整的轮仍然可以直接模拟。
然后是统一计算中间这部分,先写一下用这三个预处理的量表示出来的模拟时的式子:
(displaystylesum_{j = 1} ^ nprod_{d = 1} ^ kleft(left(rev(d) - f(j, d) ight) - x cdot del(d) ight)) 是一个关于 (x) 的 (k) 次多项式,可以将其暴力 (O(nk^2)) 计算出每一项的系数,设其为 (displaystylesum_{i = 0} ^ k c_ix^i),那么改变一下枚举顺序,上面的式子就可以变化为:
算 (displaystylesum_{x = 0} ^ {t - 1}x^i) 的时候只要用的是用 (i + 2) 个点值求出这个 (i + 1) 次多项式的大部分方法都是可行的。(如拉格朗日插值或者高斯消元,复杂度上 (k) 的指数是 (2, 3, 4) 然后带上一只或者两只 (log) 问题都不大,代码中为了方便直接写了单个 (O(n^2)) 的插值。)
Code 45 pts
#include <bits/stdc++.h>
#define LL long long
template <typename Temp>void read(Temp & res) {
Temp fh = 1; res = 0; char ch = getchar();
for(; !isdigit(ch); ch = getchar()) if(ch == '-') fh = -1;
for(; isdigit(ch); ch = getchar()) res = (res << 3) + (res << 1) + (ch ^ '0');
res = res * fh;
}
template <typename TP, typename... TPS> void read(TP & arg, TPS & ... args) {read(arg); read(args...);}
const int Maxn = 5e5 + 5;
const LL Mod = 1e9 + 7;
int n, k, w[11], c[Maxn], d[Maxn], l[11], r[11];
int e[11];
LL sum = 0;
int main() {
read(n); read(k);
for(int i = 1; i <= k; ++i) read(w[i]), l[i] = 1, r[i] = w[i];
for(int i = 1; i <= n; ++i) read(c[i]), read(d[i]), e[c[i]] += d[i];
int p = 0; bool flag = 1; for(int i = 1; i <= k; ++i) if(e[i]) flag = 0;
while(1) {
LL cur = 1;
for(int i = 1; i <= k; ++i) cur = cur * (r[i] - l[i] + 1) % Mod;
sum = (sum + cur) % Mod;
p = p % n + 1;
l[c[p]] = std::max(1, l[c[p]] + d[p]);
r[c[p]] = std::min(w[c[p]], r[c[p]] + d[p]);
if(r[c[p]] < l[c[p]]) return printf("%lld", sum), 0;
if(p == n && flag) return printf("-1"), 0;
}
return 0;
}
Code 100 pts
#include <bits/stdc++.h>
#define LL long long
template <typename Temp>void read(Temp & res) {
Temp fh = 1; res = 0; char ch = getchar();
for(; !isdigit(ch); ch = getchar()) if(ch == '-') fh = -1;
for(; isdigit(ch); ch = getchar()) res = (res << 3) + (res << 1) + (ch ^ '0');
res = res * fh;
}
template <typename TP, typename... TPS> void read(TP & arg, TPS & ... args) {read(arg); read(args...);}
const int Maxn = 5e5 + 5;
const LL Mod = 1e9 + 7;
int n, k, w[11], c[Maxn], d[Maxn], l[11], r[11];
int e[11];
LL sum = 0;
int rev[Maxn], del[Maxn], f[Maxn][11];
inline LL qpow(LL A, LL P) {
LL res = 1;
while(P) {
if(P & 1) res = res * A % Mod;
A = A * A % Mod;
P >>= 1;
}
return res;
}
LL kps[11], g[11], h[11];
int main() {
read(n, k);
for(int i = 1; i <= k; ++i) read(w[i]), l[i] = 1, r[i] = w[i];
for(int i = 1; i <= n; ++i) read(c[i], d[i]), e[c[i]] += d[i];
bool flag = 1; for(int i = 1; i <= k; ++i) if(e[i]) flag = 0;
for(int i = 1; i <= n; ++i) {
LL cur = 1; for(int j = 1; j <= k; ++j) cur = cur * (r[j] - l[j] + 1) % Mod; sum = (sum + cur) % Mod;
l[c[i]] = std::max(1, l[c[i]] + d[i]);
r[c[i]] = std::min(w[c[i]], r[c[i]] + d[i]);
if(r[c[i]] < l[c[i]]) return printf("%lld", sum), 0;
}
if(flag) return printf("-1"), 0;
for(int i = 1; i <= k; ++i) rev[i] = (r[i] - l[i] + 1);
int nl[11], nr[11]; LL nsum = sum; memcpy(nl, l, sizeof l); memcpy(nr, r, sizeof r);
for(int i = 1; i <= n; ++i) {
LL cur = 1; for(int j = 1; j <= k; ++j) cur = cur * (r[j] - l[j] + 1) % Mod;
nsum = (nsum + cur) % Mod; memcpy(f[i + 1], f[i], sizeof f[i + 1]);
if(l[c[i]] == 1 && d[i] == -1) ++f[i + 1][c[i]]; l[c[i]] = std::max(1, l[c[i]] + d[i]);
if(r[c[i]] == w[c[i]] && d[i] == 1) ++f[i + 1][c[i]]; r[c[i]] = std::min(w[c[i]], r[c[i]] + d[i]);
if(r[c[i]] < l[c[i]]) return printf("%lld", nsum), 0;
}
for(int i = 1; i <= k; ++i) del[i] = (nr[i] - nl[i] + 1) - (r[i] - l[i] + 1);
/*
for(int i = 1; i <= k; ++i) {
printf("%d %d
", rev[i], del[i]);
for(int j = 1; j <= n; ++j) printf("%d ", f[j][i]); puts("
");
}
*/
//看起来一轮完成之后剩下多少、之后每轮减少多少、走完每一轮的每一步之后减少了多少都没什么问题
int t = 0x7fffffff; for(int i = 1; i <= k; ++i) if(del[i]) t = std::min(t, (rev[i] - 1) / del[i]);
//std::cerr << t << "
";
//周期部分暴力计算代码
/*
for(int i = 0; i < t; ++i) {
for(int j = 1; j <= n; ++j) {
LL cur = 1;
for(int p = 1; p <= k; ++p) {
cur = cur * (rev[p] - i * del[p] - f[j][p]) % Mod;
}
sum = (sum + cur) % Mod;
//std::cerr << cur << '
';
}
}
*/
//背包计算 n 个多项式每项的系数
for(int i = 1; i <= n; ++i) {
memset(h, 0, sizeof h);
h[0] = (rev[1] + Mod - f[i][1]) % Mod; h[1] = Mod - del[1];
for(int j = 2; j <= k; ++j) {
LL b = (rev[j] + Mod - f[i][j]) % Mod, q = Mod - del[j];
for(int p = k; p >= 1; --p) h[p] = (h[p] * b % Mod + h[p - 1] * q % Mod) % Mod;
h[0] = h[0] * b % Mod;
}
for(int j = 0; j <= k; ++j) g[j] = (g[j] + h[j]) % Mod;
}
//插值计算 k 次自然数幂和
kps[0] = t % Mod;
for(int i = 1; i <= k; ++i) {
LL yi = 0;
for(int j = 0; j <= k + 1; ++j) {
yi = (yi + qpow(1ll * j, 1ll * i)) % Mod;
LL p1 = 1, p2 = 1;
for(int p = 0; p <= k + 1; ++p) {
if(p == j) continue;
p1 = p1 * ((t - 1 - p) % Mod + Mod) % Mod;
p2 = p2 * ((j - p) % Mod + Mod) % Mod;
}
kps[i] = (kps[i] + yi * p1 % Mod * qpow(p2, Mod - 2) % Mod) % Mod;
}
}
//for(int i = 0; i <= k; ++i) printf("%lld ", kps[i]); puts("");
for(int i = 0; i <= k; ++i) sum = (sum + kps[i] * g[i] % Mod) % Mod;
for(int i = 1; i <= k; ++i) rev[i] -= t * del[i];
for(int i = 1; i <= n; ++i) {
LL cur = 1;
for(int j = 1; j <= k; ++j) {
if(f[i][j] >= rev[j]) return printf("%lld", sum), 0;
cur = cur * (rev[j] - f[i][j]) % Mod;
}
sum = (sum + cur) % Mod;
}
return 0;
}