初二新初三集训 Week 1
8月11日
[USACO12MAR] Haybale Restacking G
思路
贪心加数学。
设 \(i\) 这块土地向左运 \(L_i\)。然后可以列出表达式。
然后按照[HAOI2008] 糖果传递推一下式子,就可以了。
代码
#include <bits/stdc++.h>
#define FASTIO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
using ll = long long;
using pii = pair<int, int>;
const int N = 1e5 + 5;
#define int ll
int a[N], b[N], c[N], n;
signed main() {
FASTIO;
cin >> n;
for (int i = 1; i <= n; ++i) {
cin >> a[i] >> b[i];
a[i] += a[i - 1], b[i] += b[i - 1];
c[i] = b[i] - a[i];
}
sort(c + 1, c + n + 1);
int k = c[(n + 1) / 2];
int ans = 0;
for (int i = 1; i <= n; ++i) {
ans += abs(k - c[i]);
}
cout << ans << '\n';
return 0;
}
[CSP-S2019] 树上的数
Link
挺好的贪心加性质题。并且可以由部分分推向正解。
首先考虑部分分。
Subtask 4 & 5
这里的图是菊花图。
手玩一下,发现菊花图其实就是原序列的一个轮换。
每次考虑一个值最小的点,找到最小的位置。细节比较多。
Subtask 2 & 3
这里的图是链。
发现,如果将 \(s\) 这个点移到 \(t\) 这个点,那么从 \(s\) 到 \(t\) 中间所有途径节点全部被确定,因为中间的边被断开。
对于每一次,找到 \(s\) 这个点是先断左边还是先断右边,维护即可。
正解
考虑对于每一个节点 \(s\) 找到它的边。那么它的边一定有先后断开的顺序。考虑将这些边编号,然后建图,设 \(i\) 连向 \(j\) 表示 \(i\) 号边在 \(j\) 号边以前断开。然后发现每次经过或出发或停止,都会产生一些断掉的链(不完整),所以判断能否成立,只需要判断能否形成一条完整的链。用并查集维护。
code
#include <bits/stdc++.h>
#define FASTIO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
using ll = long long;
using pii = pair<int, int>;
const int N = 4005;
int F[N], siz[N];
int get(int x) {
if (x == F[x]) return x;
return F[x] = get(F[x]);
}
void merge(int x, int y) {
x = get(x), y = get(y);
if (siz[x] < siz[y]) swap(x, y);
F[y] = x, siz[x] += siz[y];
}
int n, deg[N], ans[N], come[N], fa[N], ncnt = 1;
int num[N];
vector<pii> g[N];
int in[N], out[N], p[N];
bool flag[N], used[N];
int st[N], ed[N];
void clear(void) {
ncnt = 1;
for (int i = 0; i < N; ++i)
g[i].clear();
memset(deg, 0, sizeof deg);
memset(used, 0, sizeof used);
memset(st, 0, sizeof st);
memset(ed, 0, sizeof ed);
memset(in, 0, sizeof in);
memset(out, 0, sizeof out);
memset(ans, 0, sizeof ans);
}
int now;
void dfs(int u, int ff, int com) {
come[u] = com;
fa[u] = ff;
if (!ff) {
for (auto [son, id] : g[u])
if (!st[u] && !in[id] && (!ed[u] || get(ed[u]) != get(id) || siz[get(ed[u])] == deg[u]))
dfs(son, u, id ^ 1);
return;
}
if (!ed[u] && !out[com] && (!st[u] || get(st[u]) != get(com) || siz[get(st[u])] == deg[u])) {
flag[u] = 1;
}
for (auto [son, id] : g[u]) {
if (son == ff) continue;
if (get(id) != get(com) && !in[id] && !out[com] && id != st[u] && com != ed[u]) {
if ((get(com) == get(st[u]) && get(id) == get(ed[u])) || (get(com) == get(ed[u]) && get(id) == get(st[u]))) {
if (siz[get(st[u])] + siz[get(ed[u])] != deg[u]) continue;
}
dfs(son, u, id ^ 1);
}
}
}
void solve(void) {
clear();
cin >> n;
for (int i = 1; i <= n; ++i)
cin >> p[i];
for (int i = 1; i < n; ++i) {
int u, v; cin >> u >> v;
g[u].emplace_back(v, ++ncnt);
F[ncnt] = ncnt, siz[ncnt] = 1;
g[v].emplace_back(u, ++ncnt);
F[ncnt] = ncnt, siz[ncnt] = 1;
deg[u]++, deg[v]++;
}
// st[3] = 5;
for (int i = 1; i <= n; ++i)
reverse(g[i].begin(), g[i].end());
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= n; ++j)
flag[j] = 0;
now = p[i];
dfs(p[i], 0, 0);
for (int j = 1; j <= n; ++j) {
if (j != p[i] && !used[j] && flag[j]) {
ans[i] = j, ed[j] = come[j], used[j] = 1;
int las = come[j] ^ 1;
int u = fa[j];
while (u != p[i]) {
merge(come[u], las);
++out[come[u]], ++in[las];
las = come[u] ^ 1, u = fa[u];
}
// if (i == 1) cout << st[p[i]] << "----------------" << las << '\n';
st[p[i]] = las;
// if (i == 1) cout << st[p[i]] << "----------------" << las << '\n';
// if (i == 1) cout << st[p[i]] << "-------------" << las << '\n';
break;
}
}
}
for (int i = 1; i <= n; ++i) {
cout << ans[i] << ' ';
}
cout << '\n';
}
int main() {
FASTIO;
int t;
cin >> t;
while (t--)
solve();
return 0;
}
8月12日
[USACO24DEC] Cowdepenence G
思路
平衡规划。
对于 \(x < B\) 直接暴力。
对于 \(x > B\) 因为最大才 \(\frac{n}{B}\),且贡献一定是成段出现,可以二分转变的位置。
这里 \(B\) 取 \(\sqrt{n\log_2{n}}\)
代码
#include <bits/stdc++.h>
#define FASTIO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
using ll = long long;
using pii = pair<int, int>;
const int N = 1e5 + 5;
int a[N], n, ans[N];
vector<int> p[N];
void update(int l, int r, int x) {
ans[l] += x, ans[r + 1] -= x;
}
int solve(int x, int c) {
int lst = -1, ret = 0;
for (auto pos : p[c]) {
if (lst == -1) {
lst = pos;
++ret;
continue;
}
if (pos - lst > x) {
++ret;
lst = pos;
}
}
return ret;
}
int main() {
FASTIO;
cin >> n;
int B = sqrt(1.0 * n * __lg(n));
for (int i = 1; i <= n; ++i) {
cin >> a[i];
p[a[i]].push_back(i);
}
for (int i = 1; i <= n; ++i) {
if (p[i].size()) {
for (int j = 1; j <= B; ++j) {
update(j, j, solve(j, i));
}
for (int j = B + 1; j <= n; ) {
int l = j, r = n, ret = l, t = solve(j, i);
while (l <= r) {
int mid = (l + r) >> 1;
if (solve(mid, i) == t) l = mid + 1, ret = mid;
else r = mid - 1;
}
update(j, ret, t);
j = ret + 1;
}
}
}
for (int i = 1; i <= n; ++i) {
ans[i] += ans[i - 1];
cout << ans[i] << '\n';
}
return 0;
}
[USACO17DEC] Greedy Gift Takers P
思路
如果对于一头牛,它领不到礼物,那么它后面的牛也领不到。具有单调性,考虑二分。
如何 check ?想到,对于奶牛 \(i\),如果 \([1,i-1]\) 的奶牛全部到了 \(i\) 的后面即可。对 \(c_i\) 排序后模拟即可。
代码
#include <bits/stdc++.h>
#define FASTIO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
using ll = long long;
using pii = pair<int, int>;
const int N = 1e5 + 5;
int c[N], n;
bool check(int mid) {
vector<int> tmp;
for (int i = 1; i < mid; ++i)
tmp.push_back(c[i]);
sort(tmp.begin(), tmp.end());
int now = n - mid + 1;
for (int i = 0; i < mid - 1; ++i) {
if (tmp[i] < now)
now++;
else return false;
}
return true;
}
int main() {
FASTIO;
cin >> n;
for (int i = 1; i <= n; ++i) {
cin >> c[i];
}
int l = 1, r = n, best = 0;
while (l <= r) {
int mid = (l + r) >> 1;
if (check(mid)) {
l = mid + 1;
best = mid;
}
else {
r = mid - 1;
}
}
cout << n - best << '\n';
return 0;
}
[GESP202403 八级] 接竹竿
思路
容易想到,可以用倍增处理从第 \(i\) 个点开始,向后跳(就是匹配)若干次后的位置。
然后对于每个询问,一个一个跳,如果跳不动,就说明这个点要留下来,累计答案即可。
代码
#include <bits/stdc++.h>
#define FASTIO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
using ll = long long;
using pii = pair<int, int>;
const int N = 1.5e4 + 5;
const int M = 15;
int n, a[N];
int st[M][N];
vector<int> p[M];
int num[N];
void solve(void) {
memset(st, -1, sizeof st);
cin >> n;
for (int i = 1; i <= n; ++i) {
cin >> a[i];
p[a[i]].push_back(i);
num[i] = p[a[i]].size() - 1;
}
for (int i = 1; i <= n; ++i)
st[0][i] = (num[i] == int(p[a[i]].size() - 1) ? -1 : p[a[i]][num[i] + 1] + 1);
for (int i = 1; i < M; ++i)
for (int j = 1; j + (1 << (i + 1)) - 1 <= n; ++j)
if (st[i - 1][j] == -1) st[i][j] = -1;
else st[i][j] = st[i - 1][st[i - 1][j]];
int q; cin >> q;
while (q--) {
int l, r;
cin >> l >> r;
int now = l, ret = 0;
for (now = l; now <= r;) {
int nxt = 0;
for (int i = M - 1; i >= 0; --i) {
if (st[i][now] == -1) continue;
if (st[i][now] > r + 1) continue;
now = st[i][now], nxt += (1 << i);
}
// cout << now << " " << nxt << '\n';
if (!nxt) {
++ret;
++now;
}
}
cout << ret << '\n';
}
}
int main() {
FASTIO;
int t;
cin >> t;
while (t--)
solve();
return 0;
}
[ICPC 2014 WF] Surveillance
思路
考虑破环成链。转化成区间覆盖问题。第一条边和最后一条边是不好处理的。
于是可以预处理出 \(i\) 这个点出发,跳 \(2^j\) 个区间最多能覆盖到哪里。
然后直接跑贪心。
代码
#include <bits/stdc++.h>
#define FASTIO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
using ll = long long;
using pii = pair<int, int>;
const int N = 2e6 + 5;
const int M = 25;
int n, k, st[M][N];
pii seg[N];
int main() {
FASTIO;
cin >> n >> k;
for (int i = 1; i <= k; ++i) {
cin >> seg[i].first >> seg[i].second;
if (seg[i].first > seg[i].second) seg[i].second += n;
}
sort(seg + 1, seg + k + 1);
int l = 1, r = 0;
for (int i = 1; i <= 2 * n; ++i) {
while (l <= k && seg[l].first <= i) {
r = max(r, seg[l].second + 1);
l++;
}
st[0][i] = r;
}
for (int i = 1; i < M; ++i)
for (int j = 1; j <= 2 * n; ++j)
st[i][j] = st[i - 1][st[i - 1][j]];
int ans = INT_MAX;
for (int i = 1; i <= 2 * n; ++i) {
int pos = i, ret = 0;
for (int j = M - 1; j >= 0; --j) {
if (st[j][pos] - i < n) {
pos = st[j][pos];
ret += (1 << j);
}
}
pos = st[0][pos], ret++;
if (pos - i >= n) ans = min(ans, ret);
}
if (ans == INT_MAX) {
cout << "impossible\n";
}
else {
cout << ans << '\n';
}
return 0;
}
8月13日模拟赛
[USACO21OPEN] Acowdemia III B
Link
小贪心,先把横着和竖着的放在一起,然后四联通的直接随便。
代码
#include <bits/stdc++.h>
#define FASTIO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
using ll = long long;
using pii = pair<int, int>;
const int N = 1005;
int n, m, cnt;
string s[N];
map<pii, int> num, used;
int main() {
FASTIO;
cin >> n >> m;
for (int i = 1; i <= n; ++i) {
cin >> s[i];
s[i] = " " + s[i];
for (int j = 1; j <= m; ++j)
if (s[i][j] == 'C')
num[make_pair(i, j)] = ++cnt;
}
ll ans = 0;
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
if (s[i][j] == 'G') {
if (j != 1 && j != m && s[i][j - 1] == 'C' && s[i][j + 1] == 'C') {
ans ++;
continue;
}
if (i != 1 && i != n && s[i - 1][j] == 'C' && s[i + 1][j] == 'C') {
ans ++;
continue;
}
if (i != 1 && j != 1 && s[i - 1][j] == 'C' && s[i][j - 1] == 'C') {
int n1 = num[make_pair(i - 1, j)], n2 = num[make_pair(i, j - 1)];
if (!used[make_pair(n1, n2)]) {
ans ++;
used[make_pair(n1, n2)] = used[make_pair(n2, n1)] = true;
continue;
}
}
if (j != m && i != 1 && s[i - 1][j] == 'C' && s[i][j + 1] == 'C') {
int n1 = num[make_pair(i - 1, j)], n2 = num[make_pair(i, j + 1)];
if (!used[make_pair(n1, n2)]) {
ans ++;
used[make_pair(n1, n2)] = used[make_pair(n2, n1)] = true;
continue;
}
}
if (i != n && j != 1 && s[i + 1][j] == 'C' && s[i][j - 1] == 'C') {
int n1 = num[make_pair(i + 1, j)], n2 = num[make_pair(i, j - 1)];
if (!used[make_pair(n1, n2)]) {
ans ++;
used[make_pair(n1, n2)] = used[make_pair(n2, n1)] = true;
continue;
}
}
if (j != m && i != n && s[i + 1][j] == 'C' && s[i][j + 1] == 'C') {
int n1 = num[make_pair(i + 1, j)], n2 = num[make_pair(i, j + 1)];
if (!used[make_pair(n1, n2)]) {
ans ++;
used[make_pair(n1, n2)] = used[make_pair(n2, n1)] = true;
continue;
}
}
}
}
}
cout << ans << '\n';
return 0;
}
[GCJ 2018 #1B] Transmutation
Link
挺板的二分题,直接跑反图,没什么说的。
代码
#include <bits/stdc++.h>
#define FASTIO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
using ll = long long;
using pii = pair<ll, ll>;
const int N = 105;
#define int ll
int n, l[N], r[N];
int have[N], hh[N];
int vis[N];
bool check(ll mid) {
for (int i = 1; i <= n; ++i) {
hh[i] = have[i];
vis[i] = 0;
}
if (mid == hh[1]) return true;
queue<pii> q;
q.push(make_pair(1, mid - hh[1]));
hh[1] = 0,vis[1] = 1;
while (!q.empty()) {
auto x = q.front();
if (vis[x.first] > 9e2) return false;
int u = x.first, need = x.second;
if (need <= hh[u]) {
hh[u] -= need;
q.pop();
continue;
}
need -= hh[u];
hh[u] = 0;
q.push({l[u], need});
q.push({r[u], need});
vis[l[u]]++;
vis[r[u]]++;
q.pop();
}
return true;
}
int cas = 0;
void solve(void) {
cin >> n;
for (int i = 1; i <= n; ++i) {
cin >> l[i] >> r[i];
}
ll sum = 0;
for (int i = 1; i <= n; ++i) {
cin >> have[i];
sum += have[i];
}
ll l = have[1], r = 1e11, best = have[1];
while (l <= r) {
ll mid = (l + r) >> 1;
if (check(mid)) {
best = mid;
l = mid + 1;
}
else {
r = mid - 1;
}
}
cout << "Case #" << ++cas << ": " << best << '\n';
}
signed main() {
FASTIO;
int t;
cin >> t;
while (t--)
solve();
return 0;
}
[COCI 2021/2022 #2] Magneti
Link
插入 DP,考虑设 \(dp_{i,j,k}\)表示前 \(i\) 个磁铁,分成 \(j\) 个组,目前间隔和为 \(k\),很容易转移。
代码
#include <bits/stdc++.h>
#define FASTIO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
using ll = long long;
using pii = pair<int, int>;
const int N = 55;
const int M = 1e4 + 5;
const int Md = 1e9 + 7;
int n, l, r[N];
ll fac[M], rev[M];
ll dp[2][N][M];
ll qpow(ll a, ll b) {
ll ret = 1;
while (b) {
if (b & 1)
ret = (ret * a) % Md;
a = (a * a) % Md;
b >>= 1;
}
return ret;
}
void init(void) {
fac[0] = rev[0] = 1;
for (int i = 1; i < M; ++i) {
fac[i] = (fac[i - 1] * i * 1ll) % Md;
rev[i] = (rev[i - 1] * qpow(i, Md - 2) * 1ll) % Md;
}
}
ll C(int n, int m) {
if (n < m) return 0;
return fac[n] * rev[m] % Md * rev[n - m] % Md;
}
int main() {
FASTIO;
// freopen("magnet.in", "r", stdin);
// freopen("magnet.out", "w", stdout);
init();
cin >> n >> l;
for (int i = 1; i <= n; ++i) cin >> r[i], --r[i];
sort(r + 1, r + n + 1);
dp[0][0][0] = 1;
for (int i = 1; i <= n; i++) {
memset(dp[i & 1], 0, sizeof dp[i & 1]);
for (int j = 1; j <= i; j++) {
for (int k = 0; k <= l; k++) {
dp[i & 1][j][k] = dp[i - 1 & 1][j - 1][k] * j % Md;
if (k >= r[i])
dp[i & 1][j][k] = (dp[i & 1][j][k] + dp[i - 1 & 1][j][k - r[i]] * j % Md * 2 % Md) % Md;
if (k >= 2 * r[i])
dp[i & 1][j][k] = (dp[i & 1][j][k] + dp[i - 1 & 1][j + 1][k - 2 * r[i]] * j % Md) % Md;
}
}
}
ll ans = 0;
for (int i = 0; i <= l; ++i) {
ans = (ans + dp[n & 1][1][i] * C(l - i, n) % Md) % Md;
}
cout << ans << '\n';
return 0;
}
[IOI 2016] railroad
Link
咕咕咕。
8月14日
[NOIP2023] 三值逻辑
Link
建边表示相等或不等关系,然后对每个联通块跑二分图匹配,如果不是二分图,这个联通块里面所有的点的值都是 U。
代码
#include <bits/stdc++.h>
#define memset0(a) memset(a, 0, sizeof a)
using namespace std;
typedef pair<int, int> pii;
const int MAXN = 1e5 + 5;
int n, m;
int T, U;
int fa[MAXN];
bool flp[MAXN];
vector<pii> G1[MAXN];
bool vis[MAXN];
int f[MAXN];
bool is_bi_graph;
int get_size(int u) {
vis[u] = 1;int siz = 1;
for (auto [v, w] : G1[u]) {
if (vis[v]) {
if (f[v] != (f[u] ^ w))
is_bi_graph = 0;
}
else {
f[v] = (f[u] ^ w);
siz += get_size(v);
}
}
return siz;
}
vector<int> G2[MAXN];
int get_U_size(int u = U) {
int siz = 1;
for (auto v : G2[u])
siz += get_U_size(v);
return siz;
}
int main() {
ios::sync_with_stdio(0); cin.tie(0);
int C, number_of_cases; cin >> C >> number_of_cases;
while (number_of_cases--) {
cin >> n >> m;
T = n + 1, U = n + 2;
for (int i = 1; i <= n + 2; i++)
fa[i] = i, flp[i] = 0;
memset0(G1); memset0(G2); memset0(f); memset0(vis);
while (m--) {
char op; cin >> op;
if (op == 'T' || op == 'F' || op == 'U') {
int x; cin >> x;
if (op == 'T') fa[x] = T, flp[x] = 0;
else if (op == 'F') fa[x] = T, flp[x] = 1;
else if (op == 'U') fa[x] = U, flp[x] = 0;
}
else if (op == '+') {
int x, y; cin >> x >> y;
fa[x] = fa[y], flp[x] = flp[y];
}
else if (op == '-') {
int x, y; cin >> x >> y;
fa[x] = fa[y], flp[x] = (flp[y] ^ 1);
}
}
for (int u = 1; u <= n; u++) {
G1[fa[u]].emplace_back(u, flp[u]);
G1[u].emplace_back(fa[u], flp[u]);
}
int ans = 0;
for (int u = 1; u <= n; u++)if (!vis[u]) {
is_bi_graph = 1;
int tmp = 0;
f[u] = 1, tmp = get_size(u);
if (!is_bi_graph)
ans += tmp;
}
for (int u = 1; u <= n; u++)
G2[fa[u]].push_back(u);
ans += get_U_size() - 1;
cout << ans << '\n';
}
return 0;
}
[CSP-S 2022] 星战
Link
可以发现,判断的条件为内向基环树。出度较难维护,而入度较好维护。而在有向图中,入度等于出度。
用随机数给每个点赋点权,然后用数组维护入度。判断即可。
代码
#include <bits/stdc++.h>
#define FASTIO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
using ll = long long;
using pii = pair<int, int>;
const int N = 5e5 + 5;
ll deg[N], w[N], sum, g[N], target, now;
int n, m;
mt19937 rnd(time(0));
int main() {
FASTIO;
cin >> n >> m;
ll now = 0;
for (int i = 1; i <= n; ++i) {
w[i] = rnd();
sum += w[i];
}
while (m--) {
int u, v;
cin >> u >> v;
now += w[u];
deg[v] += w[u];
g[v] += w[u];
}
int q;
cin >> q;
while (q--) {
int opt, u, v;
cin >> opt >> u;
if (opt == 1) {
cin >> v;
deg[v] -= w[u];
now -= w[u];
}
else if (opt == 2) {
now -= deg[u];
deg[u] = 0;
}
else if (opt == 3) {
cin >> v;
deg[v] += w[u];
now += w[u];
}
else {
now += g[u] - deg[u];
deg[u] = g[u];
}
if (now == sum) {
cout << "YES\n";
}
else {
cout << "NO\n";
}
}
return 0;
}
8月15日
[CSP-J 2023] 旅游巴士
Link
分层图,由于在某一个点等待可以等价于在起点等待。所以可以将图分成 \(k\) 层,最短路即可。
代码
#include <bits/stdc++.h>
#define REP(i, l, r) for (int i = (l); i <= (r); ++ i)
#define DEP(i, r, l) for (int i = (r); i >= (l); -- i)
#define fi first
#define se second
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
const int N = 1e6 + 5;
LL n, m, k, u, v, w, dis[N];
vector<pii> G[N];
bool vis[N];
int id(int u, int k) { return n * k + u; }
LL updiv(LL a, LL b) { return (a + b - 1) / b; }
void Dji() {
memset(dis, 0x3f, sizeof dis);
priority_queue<pii, vector<pii>, greater<pii>> q;
dis[1] = 0, q.push(pii(dis[1], 1));
while (!q.empty()) {
pii tmp = q.top(); q.pop();
int u = tmp.se;
if (vis[u]) continue;
vis[u] = 1;
for (pii qwq : G[(u - 1) % n + 1]) {
int v = id(qwq.fi, (dis[u] + 1) % k), d = qwq.se;
int w = max(updiv(d - dis[u], k), 0LL) * k + 1;
if (dis[v] > dis[u] + w) {
dis[v] = dis[u] + w;
if (!vis[v]) q.push(pii(dis[v], v));
}
}
}
}
int main() {
cin >> n >> m >> k;
for (int i = 1; i <= m; ++i) {
cin >> u >> v >> w, G[u].push_back(pii(v, w));
}
Dji();
if (dis[n] > 1e9) cout << "-1\n";
else cout << dis[n] << '\n';
return 0;
}
[NOIP 2017 提高组] 逛公园
Link
水题,设 \(dp_{i,j}\) 表示从 \(1\) 到 \(i\) 节点,比最短路长 \(j\) 的方案数。建反图,记忆化搜索。
代码
#include <bits/stdc++.h>
#define FASTIO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
using ll = long long;
#define int ll
using pii = pair<int, int>;
const int N = 1e5 + 5;
const int K = 55;
int n, m, k, Md;
vector<pii> g[N], re[N];
ll dp[N][K], dis[N];
bool mark[N][K], vis[N][K];
void Dijkstra(void) {
priority_queue<pii, vector<pii>, greater<pii>> q;
memset(dis, 0x3f, sizeof dis);
dis[1] = 0;
q.push({ 0, 1 });
while (!q.empty()) {
int u = q.top().second;
q.pop();
for (auto edge : re[u]) {
int v = edge.first, w = edge.second;
if (dis[v] > dis[u] + w) {
dis[v] = dis[u] + w;
q.push({ dis[v], v });
}
}
}
}
void trans(ll& a, ll b) {
b %= Md;
a %= Md;
a = (a + b) % Md;
}
bool zero;
int dfs(int u, int k) {
if (vis[u][k]) {
// cout << "Error : " << u << " " << k << '\n';
zero = true;
return 0;
}
if (mark[u][k]) return dp[u][k];
mark[u][k] = true;
vis[u][k] = true;
int tar = k + dis[u];
for (auto edge : g[u]) {
int v = edge.first, w = edge.second;
int tmp = tar - w - dis[v];
if (tmp > 50 || tmp < 0) continue;
// cout << tar << ' ' << w << ' ' << dis[u] << '\n';
// cout << "From : " << "(" << u << " , " << k << ") to : " << "(" << v << " , " << tmp << ")\n";
trans(dp[u][k], dfs(v, tmp));
if (zero) {
vis[u][k] = false;
return 0;
}
}
vis[u][k] = false;
// if (u == 1) return k == 0;
return dp[u][k];
}
void solve(void) {
zero = false;
for (int i = 1; i <= n; ++i)
g[i].clear(), re[i].clear();
cin >> n >> m >> k >> Md;
for (int i = 1; i <= m; ++i) {
int u, v, w;
cin >> u >> v >> w;
g[v].emplace_back(u, w);
re[u].emplace_back(v, w);
}
Dijkstra();
// for (int i = 1; i <= n; ++i) {
// cout << dis[i] << ' ';
// }
// cout << '\n';
// cout << dis[3] << '\n';
memset(dp, 0, sizeof dp);
memset(mark, 0, sizeof mark);
dfs(1, 0);
if (zero) {
cout << -1 << '\n';
return;
}
memset(dp, 0, sizeof dp);
memset(mark, 0, sizeof mark);
mark[1][0] = 1;
dp[1][0] = 1;
// dfs(5, 0);
ll ans = 0;
for (int i = 0; i <= k; ++i) {
memset(vis, 0, sizeof vis);
trans(ans, dfs(n, i));
if (zero) break;
}
if (zero) cout << -1 << '\n';
else cout << ans << '\n';
}
signed main() {
FASTIO;
int t;
cin >> t;
while (t--)
solve();
return 0;
}
/*
Input :
1
5 10 0 624775377
1 2 1
2 5 2
2 4 1
5 4 2
4 2 1
4 5 2
2 3 1
3 2 1
1 3 2
3 5 1
Output :
3
*/
8月16日模拟赛
[COCI 2019/2020 #5] Putovanje
Link
赛时胡树剖,直接挂分。可以将一条路径分成两节,然后树上差分。
代码
#include <bits/stdc++.h>
#define FASTIO \
ios::sync_with_stdio(false); \
cin.tie(0); \
cout.tie(0);
using namespace std;
using ll = long long;
#define int ll
using pii = pair<int, int>;
const int N = 2e5 + 5;
vector<pii> g[N];
ll c1[N], c2[N], cnt[N];
int n, up[N], tp[N], dfn[N], siz[N], son[N], dcnt, dep[N];
int fid[N];
struct node {
int tag, sum;
int tl, tr;
} tree[N << 2];
void dfs1(int u, int fa) {
// cout << u << ' ' << fa << '\n';
siz[u] = 1;
son[u] = -1;
int mx = -1;
up[u] = fa;
dep[u] = dep[fa] + 1;
for (auto edge : g[u]) {
int v = edge.first;
if (v == fa) {
fid[u] = edge.second;
continue;
}
dfs1(v, u);
siz[u] += siz[v];
if (mx < siz[v]) {
mx = siz[v];
son[u] = v;
}
}
}
void dfs2(int u, int fa, int top) {
// if (u) {
// cout << u << ' ' << fa << ' ' << top << ' ' << son[u] << '\n';
// }
dfn[u] = ++dcnt;
tp[u] = top;
if (son[u] != -1)
dfs2(son[u], u, top);
for (auto edge : g[u]) {
int v = edge.first;
if (v == fa || v == son[u])
continue;
dfs2(v, u, v);
}
}
#define ls(id) (id << 1)
#define rs(id) (id << 1 | 1)
void merge(int id) { tree[id].sum = (tree[ls(id)].sum + tree[rs(id)].sum); }
void pushdown(int id) {
if (tree[id].tag) {
tree[ls(id)].tag += tree[id].tag;
tree[rs(id)].tag += tree[id].tag;
tree[ls(id)].sum += tree[id].tag * (tree[ls(id)].tr - tree[ls(id)].tl + 1);
tree[rs(id)].sum += tree[id].tag * (tree[rs(id)].tr - tree[rs(id)].tl + 1);
tree[id].tag = 0;
}
}
void build(int id, int tl, int tr) {
tree[id].tl = tl, tree[id].tr = tr;
tree[id].tag = tree[id].sum = 0;
if (tl == tr)
return;
int mid = (tl + tr) >> 1;
build(ls(id), tl, mid);
build(rs(id), mid + 1, tr);
}
void update(int id, int l, int r, int add) {
if (tree[id].tl > r || tree[id].tr < l)
return;
if (tree[id].tl >= l && tree[id].tr <= r) {
tree[id].tag += add;
tree[id].sum += add * (tree[id].tr - tree[id].tl + 1);
return;
}
pushdown(id);
int mid = (tree[id].tl + tree[id].tr) >> 1;
update(ls(id), l, r, add);
update(rs(id), l, r, add);
merge(id);
}
int query(int id, int pos) {
if (tree[id].tl == tree[id].tr)
return tree[id].sum;
pushdown(id);
int mid = (tree[id].tl + tree[id].tr) >> 1;
if (pos <= mid)
return query(ls(id), pos);
else
return query(rs(id), pos);
}
#undef ls
#undef rs
void add(int u, int v) {
while (tp[u] != tp[v]) {
// cout << u << ' ' << v << '\n';
if (dep[tp[u]] < dep[tp[v]])
swap(u, v);
update(1, dfn[tp[u]], dfn[u], 1);
u = up[tp[u]];
}
if (v == u)
return;
if (dep[v] < dep[u])
update(1, dfn[v] + 1, dfn[u], 1);
else
update(1, dfn[u] + 1, dfn[v], 1);
}
signed main() {
FASTIO;
// freopen("travel.in", "r", stdin);
// freopen("travel.out", "w", stdout);
cin >> n;
for (int i = 1; i < n; ++i) {
int u, v;
cin >> u >> v >> c1[i] >> c2[i];
g[u].push_back({ v, i });
g[v].push_back({ u, i });
}
dfs1(1, 0);
dfs2(1, 0, 1);
build(1, 1, n);
// for (int i = 1; i <= n; ++i) {
// if (tp[i] == 1) {
// cout << i << '\n';
// }
// }
for (int i = 1; i < n; ++i) add(i, i + 1);
// cout << "---------------------\n";
ll ret = 0;
for (int i = 2; i <= n; ++i) {
int cnt = query(1, dfn[i]);
if (cnt * c1[fid[i]] > c2[fid[i]])
ret += c2[fid[i]];
else
ret += c1[fid[i]] * cnt;
}
cout << ret << '\n';
return 0;
}
/*
1
317
420
445
451
588
653
741
813
867
908
1035
1089
1157
1178
1291
1318
1428
1446
1508
1572
1636
1667
1672
*/
crontab
Link
大模拟,挺恶心。
代码
#include <bits/stdc++.h>
#define FASTIO \
ios::sync_with_stdio(false); \
cin.tie(0); \
cout.tie(0);
using namespace std;
using ll = long long;
using pii = pair<int, int>;
const int d[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
int days(int year, int month) {
if (month != 2)
return d[month];
if (year % 400 == 0)
return d[month] + 1;
if (year % 4 == 0 && year % 100 != 0)
return d[month] + 1;
return d[month];
}
int get_date(int year, int month, int day) {
if (month <= 2) {
year--;
month += 12;
}
return (day + (13 * (month + 1) / 5) + year % 100 + (year % 100 / 4) + (year / 100 / 4) +
5 * (year / 100) + 6) %
7;
}
struct task {
int year, month, day, date;
string name;
};
vector<task> li;
int ans = 0;
map<string, int> mp;
void init(void) {
mp["MON"] = 1, mp["TUE"] = 2, mp["WED"] = 3, mp["THU"] = 4, mp["FRI"] = 5, mp["SAT"] = 6, mp["SUN"] = 0;
}
struct ti {
int year, month, day, date;
void add(void) {
date = (date + 1) % 7;
if (day == days(year, month)) {
day = 1;
if (month == 12) {
year++;
month = 1;
} else
month++;
} else
day++;
}
bool operator<(const ti &o) const {
if (year == o.year)
if (month == o.month)
if (day == o.day)
return false;
else
day < o.day;
else
month < o.month;
else
return year < o.year;
}
bool operator>(const ti &o) const {
if (year == o.year)
if (month == o.month)
if (day == o.day)
return false;
else
day > o.day;
else
month > o.month;
else
return year > o.year;
}
bool operator==(const ti &o) const { return year == o.year && o.month == month && o.day == day; }
} st, ed;
ti getlast(int year) { return { year, 12, 31, get_date(year, 12, 31) }; }
ti getfirst(int year) { return { year, 1, 1, get_date(year, 1, 1) }; }
int n;
bool check(task x, ti y) {
if (x.year != -1 && y.year != x.year)
return false;
if (x.month != -1 && x.month != y.month)
return false;
if (x.day != -1 && x.day != y.day)
return false;
if (x.date != -1 && x.date != y.date)
return false;
return true;
}
struct ans_node {
string ti, name;
int id;
bool operator < (const ans_node &o) const {
if (o.ti == ti) {
return id < o.id;
}
return ti < o.ti;
}
};
int main() {
FASTIO;
// freopen("wyswd.in", "r", stdin);
// freopen("wyswd.out", "w", stdout);
init();
string tmp, date;
int y = 0;
cin >> tmp >> date;
for (int i = 0; i < 8; ++i) y = (y * 10 + (tmp[i] - '0'));
st.year = y;
y = 0;
for (int i = 8; i < 10; ++i) y = (y * 10 + (tmp[i] - '0'));
st.month = y;
y = 0;
for (int i = 10; i < 12; ++i) y = (y * 10 + (tmp[i] - '0'));
st.day = y;
st.date = mp[date];
y = 0;
cin >> tmp >> date;
for (int i = 0; i < 8; ++i) y = (y * 10 + (tmp[i] - '0'));
ed.year = y;
y = 0;
for (int i = 8; i < 10; ++i) y = (y * 10 + (tmp[i] - '0'));
ed.month = y;
y = 0;
for (int i = 10; i < 12; ++i) y = (y * 10 + (tmp[i] - '0'));
ed.day = y;
ed.date = mp[date];
cin >> n;
for (int i = 1; i <= n; ++i) {
task now;
cin >> tmp;
if (tmp == "*")
now.day = -1;
else
now.day = stoi(tmp);
cin >> tmp;
if (tmp == "*")
now.month = -1;
else
now.month = stoi(tmp);
cin >> tmp;
if (tmp == "*")
now.year = -1;
else
now.year = stoi(tmp);
cin >> tmp;
if (tmp == "*")
now.date = -1;
else
now.date = mp[tmp];
cin >> tmp;
now.name = tmp;
li.push_back(now);
// cout << now.year << ' ' << now.month << ' ' << now.day << ' ' << now.date << ' ' <<
//now.name << '\n';
}
ed.add();
ti tp = (ed < getfirst(st.year + 1) ? ed : getfirst(st.year + 1));
for (ti i = st; i < tp; i.add()) {
for (auto x : li) {
if (check(x, i)) {
string now = "";
tmp = to_string(i.year);
for (int j = 1; j <= 8 - tmp.size(); ++j) now += "0";
now += tmp;
tmp = to_string(i.month);
for (int j = 1; j <= 2 - tmp.size(); ++j) now += "0";
now += tmp;
tmp = to_string(i.day);
for (int j = 1; j <= 2 - tmp.size(); ++j) now += "0";
now += tmp;
++ans;
cout << now << " " << x.name << '\n';
}
}
}
if (ed.year == st.year) {
if (!ans) {
cout << "Empty\n";
}
return 0;
}
for (int year = st.year + 1; year < ed.year; ++year) {
int id = 0;
vector<ans_node> lp;
for (auto x : li) {
++id;
if (x.year != -1 && x.year != year)
continue;
if (x.month != -1) {
if (x.date == -1 || get_date(year, x.month, x.day) == x.date) {
string now = "";
tmp = to_string(year);
for (int j = 1; j <= 8 - tmp.size(); ++j) now += "0";
now += tmp;
tmp = to_string(x.month);
for (int j = 1; j <= 2 - tmp.size(); ++j) now += "0";
now += tmp;
tmp = to_string(x.day);
for (int j = 1; j <= 2 - tmp.size(); ++j) now += "0";
now += tmp;
++ans;
// cout << now << " " << x.name << '\n';
lp.push_back({now, x.name, id});
}
} else {
for (int month = 1; month <= 12; ++month) {
if (x.date == -1 || get_date(year, month, x.day) == x.date) {
string now = "";
tmp = to_string(year);
for (int j = 1; j <= 8 - tmp.size(); ++j) now += "0";
now += tmp;
tmp = to_string(month);
for (int j = 1; j <= 2 - tmp.size(); ++j) now += "0";
now += tmp;
tmp = to_string(x.day);
for (int j = 1; j <= 2 - tmp.size(); ++j) now += "0";
now += tmp;
++ans;
lp.push_back({now, x.name, id});
}
}
}
}
sort(lp.begin(), lp.end());
for (auto x : lp) {
cout << x.ti << ' ' << x.name << '\n';
}
}
for (ti i = getfirst(ed.year); i < ed; i.add()) {
for (auto x : li) {
if (check(x, i)) {
string now = "";
tmp = to_string(i.year);
for (int j = 1; j <= 8 - tmp.size(); ++j) now += "0";
now += tmp;
tmp = to_string(i.month);
for (int j = 1; j <= 2 - tmp.size(); ++j) now += "0";
now += tmp;
tmp = to_string(i.day);
for (int j = 1; j <= 2 - tmp.size(); ++j) now += "0";
now += tmp;
++ans;
cout << now << " " << x.name << '\n';
}
}
}
if (!ans)
cout << "Empty\n";
return 0;
}
8月17日
咕咕咕

浙公网安备 33010602011771号