初二新初三集训 Week 1

8月11日

[USACO12MAR] Haybale Restacking G

Link

思路

贪心加数学。
\(i\) 这块土地向左运 \(L_i\)。然后可以列出表达式。

\[A_i - L_i + L_{i + 1}=B_i \hspace{0.2cm}(for \hspace{0.2cm} 1\leq i \leq n-1) \]

\[A_n-L_n+L_1=B_n \]

然后按照[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

Link

思路

平衡规划。
对于 \(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

Link

思路

如果对于一头牛,它领不到礼物,那么它后面的牛也领不到。具有单调性,考虑二分。
如何 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 八级] 接竹竿

Link

思路

容易想到,可以用倍增处理从第 \(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

Link

思路

考虑破环成链。转化成区间覆盖问题。第一条边和最后一条边是不好处理的。
于是可以预处理出 \(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日

咕咕咕

posted @ 2025-08-12 22:49  tanjiaqi  阅读(13)  评论(0)    收藏  举报