USACO2026 JAN1 Silver 简要题解
预估难度:*1600, *2100, *1900。对应洛谷黄蓝绿。
2.5 h 慢慢悠悠 AK 了,中间还去洗了个澡,应该是比较简单的 Silver 了。
A
打表发现编号为 \(1\) 的牛最多会成为 \(100\) 次队头,所以 \(op = 1\) 的询问可以暴力做。
\(op = 2\) 的询问则需要发现成为队头的时间的递推式,具体而言,假设 \(t\) 时刻某头牛成为了队头,那么当 \(T'=t + 1 + \lfloor\dfrac{t + 1}{2}\rfloor\) 时这头牛第二次成为队头。所以可以先求出编号为 \(x\) 的牛下次成为队头是什么时候,然后不断推回去,直到不能推为止。由 \(T\) 推导 \(t\) 可以用一个偷懒的方式,先忽略下取整的限制,正常解出方程,然后在解附近的几个整数全都 check 一遍即可。最后根据“编号为 \(x\) 的牛第一次成为队头的时间为 \(3x - 1\)”的结论可以得到牛的编号,结论的证明可以自己打表手模。
时间复杂度 \(O(TB)\),其中 \(B = 100\)。
#include <bits/stdc++.h>
#define fi first
#define se second
#define eb(x) emplace_back(x)
#define pb(x) push_back(x)
#define lc(x) (tr[x].ls)
#define rc(x) (tr[x].rs)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
typedef __int128 i128;
using pi = pair<int, int>;
void solve1(ll x, ll t)
{
if(t <= 2 * x - 1)
{
cout << x << "\n";
return;
}
if(t <= 3 * x - 1)
{
cout << x - (t - (2 * x - 1)) << "\n";
return;
}
ll nowt = 3 * x - 1;
while(1)
{
ll to = nowt + (nowt + 1) / 2 + 1;
if(to >= t) break;
nowt = to;
}
cout << (nowt + 1) / 2 - (t - nowt - 1) << "\n";
}
bool check(ll x, ll y)
{
return (x + (x + 1) / 2 + 1 == y);
}
void solve2(ll x, ll t)
{
if(x > t / 2)
{
cout << x << "\n";
return;
}
t += x;
while(1)
{
ll y = (2 * t - 3) / 3;
bool isfind = 0;
for(ll i = y - 2; i <= y + 2; i++)
{
if(check(i, t))
{
t = i;
isfind = 1;
break;
}
}
if(!isfind) break;
}
cout << (t + 1) / 3 << "\n";
}
int main()
{
//freopen("sample.in", "r", stdin);
//freopen("sample.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
while(t--)
{
ll op, x, y;
cin >> op >> x >> y;
if(op == 1) solve1(x, y);
else solve2(x, y);
}
return 0;
}
B
先考虑如何判断是否有解。
显然可以图论建模,然后对每个连通块随出一颗生成树出来,递推出每个数与根的关系 \(kx+b\),其中 \(k\in\{1, -1\}\)。
对于剩余的边,若 \(k_u\ne k_v\),判断是否满足 \(b_u+b_v=w\);若 \(k_u= k_v\),则可以直接推出根的值 \(\dfrac{w - b_u-b_v}{2k}\),判断合法性,对这个连通块打上一个标记即可。
现在考虑加了区间限制之后的做法,对每个连通块考虑,此时若根的值已经确定,则可以直接计算贡献;否则遍历连通块里的每一个点,根据与根的关系推出这个点满足要求时根节点的值的区间 \([l', r']\),然后在根节点用 map 维护差分贡献区间,最后在根节点处把最大的差分值当做该连通块的最大贡献即可。
难点在于代码。时间复杂度 \(O(n\log n)\)。
#include <bits/stdc++.h>
#define fi first
#define se second
#define eb(x) emplace_back(x)
#define pb(x) push_back(x)
#define lc(x) (tr[x].ls)
#define rc(x) (tr[x].rs)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
typedef __int128 i128;
using pi = pair<int, int>;
using pii = pair<pi, int>;
const int N = 200005, M = 400005;
ll n, m, lx[N], rx[N], kx[N], bx[N], extval[N], haveval[N];
vector<pi> g[N];
vector<pii> gx;
struct DSU{
int fa[N];
void init()
{
for(int i = 1; i <= n; i++) fa[i] = i;
}
int findf(int x)
{
if(fa[x] != x) fa[x] = findf(fa[x]);
return fa[x];
}
void combine(int x, int y)
{
int fx = findf(x), fy = findf(y);
fa[fx] = fy;
}
} dsu;
void dfs(int u, int fa)
{
for(auto eg : g[u])
{
ll v = eg.fi, w = eg.se;
if(v == fa) continue;
kx[v] = -kx[u];
bx[v] = w - bx[u];
dfs(v, u);
}
}
map<ll, ll> tot[N];
void solve()
{
cin >> n >> m;
dsu.init();
for(int i = 1; i <= n; i++)
{
g[i].clear();
tot[i].clear();
}
gx.clear();
memset(kx, 0, sizeof(kx));
memset(bx, 0, sizeof(bx));
memset(extval, 0, sizeof(extval));
memset(haveval, 0, sizeof(haveval));
for(int i = 1; i <= n; i++) cin >> lx[i];
for(int i = 1; i <= n; i++) cin >> rx[i];
for(int i = 1; i <= m; i++)
{
ll u, v, w;
cin >> u >> v >> w;
if(dsu.findf(u) == dsu.findf(v))
{
gx.push_back({{u, v}, w});
continue;
}
g[u].push_back({v, w});
g[v].push_back({u, w});
dsu.combine(u, v);
}
for(int i = 1; i <= n; i++)
{
if(dsu.findf(i) == i)
{
kx[i] = 1;
bx[i] = 0;
dfs(i, 0);
}
}
for(auto eg : gx)
{
ll u = eg.fi.fi, v = eg.fi.se, w = eg.se;
int anc = dsu.findf(u);
if(kx[u] != kx[v])
{
if(bx[u] + bx[v] != w)
{
cout << "-1\n";
return;
}
continue;
}
ll ext = 0;
if(kx[u] == -1)
{
if((bx[u] + bx[v] - w) % 2)
{
cout << "-1\n";
return;
}
ext = (bx[u] + bx[v] - w) / 2;
}
else
{
if((w - bx[u] - bx[v]) % 2)
{
cout << "-1\n";
return;
}
ext = (w - bx[u] - bx[v]) / 2;
}
if(haveval[anc] && ext != extval[anc])
{
cout << "-1\n";
return;
}
extval[anc] = ext;
haveval[anc] = 1;
}
ll ans = 0;
for(int i = 1; i <= n; i++)
{
int anc = dsu.findf(i);
if(haveval[anc])
{
ll val = kx[i] * extval[anc] + bx[i];
if(lx[i] <= val && val <= rx[i]) ans++;
continue;
}
if(kx[i] > 0)
{
tot[anc][lx[i] - bx[i]]++;
tot[anc][rx[i] - bx[i] + 1]--;
}
else
{
tot[anc][bx[i] - rx[i]]++;
tot[anc][bx[i] - lx[i] + 1]--;
}
}
for(int i = 1; i <= n; i++)
{
int anc = dsu.findf(i);
if(haveval[anc]) continue;
if(anc != i) continue;
ll pre = 0, mx = 0;
for(auto itm : tot[i])
{
pre += itm.se;
mx = max(mx, pre);
}
ans += mx;
}
cout << ans << "\n";
}
int main()
{
//freopen("sample.in", "r", stdin);
//freopen("sample.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
while(t--) solve();
return 0;
}
C
题意相当于是给定了所有长度为 \(k\) 的区间的异或和,求出 \(1\) 的个数的最大值、最小值。
对区间 \([l, r], [l + 1, r + 1]\) 考虑,若两区间 \(r\) 值相同,说明 \(b_l=b_{r + 1}\);否则说明 \(b_l\ne b_{r + 1}\)。这显然可以用并查集维护。
在满足上面的要求后,最后满足 \(\oplus_{i = 1}^k b_i=r_1\) 的要求就能使得字符串合法。但是我们先不考虑这个限制,先贪心地对每个连通块贪出最优解,然后再调整方案使其满足这个限制。容易发现我们最后最多只会调整一个连通块,所以在调整的代价里选取一个最小值 / 最大值即可。
时间复杂度 \(O(n)\)。
#include <bits/stdc++.h>
#define fi first
#define se second
#define eb(x) emplace_back(x)
#define pb(x) push_back(x)
#define lc(x) (tr[x].ls)
#define rc(x) (tr[x].rs)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
typedef __int128 i128;
using pi = pair<int, int>;
const int N = 1000005, inf = 0x3f3f3f3f;
int n, k, a[N];
int fa[2 * N], smsz[2 * N], sza[2 * N];
void init()
{
for(int i = 1; i <= 2 * n; i++)
{
fa[i] = i;
smsz[i] = 1;
if(i <= n) sza[i] = 1;
else sza[i] = 0;
}
}
int findf(int x)
{
if(fa[x] != x) fa[x] = findf(fa[x]);
return fa[x];
}
void combine(int x, int y)
{
int fx = findf(x), fy = findf(y);
if(fx == fy) return;
fa[fx] = fy;
smsz[fy] += smsz[fx];
sza[fy] += sza[fx];
}
void solve()
{
cin >> n >> k;
init();
for(int i = 1; i <= n - k + 1; i++)
{
char c;
cin >> c;
a[i] = c - '0';
}
if(k == 1)
{
int ans = 0;
for(int i = 1; i <= n; i++) ans += a[i];
cout << ans << " " << ans << "\n";
return;
}
for(int i = 1; i < n - k + 1; i++)
{
int u = i, v = i + k;
if(a[i] == a[i + 1])
{
combine(u, v);
combine(u + n, v + n);
}
else
{
combine(u, v + n);
combine(u + n, v);
}
}
int mn = 0, mx = 0, choose = 0, mxchange = -inf;
for(int i = 1; i <= k; i++)
{
int anc = findf(i);
int va = sza[anc], vb = smsz[anc] - sza[anc];
if(va > vb)
{
choose ^= 1;
mx += va;
mxchange = max(mxchange, vb - va);
}
else
{
mx += vb;
mxchange = max(mxchange, va - vb);
}
}
if(choose != a[1]) mx += mxchange;
mxchange = inf;
choose = 0;
for(int i = 1; i <= k; i++)
{
int anc = findf(i);
int va = sza[anc], vb = smsz[anc] - sza[anc];
if(va > vb)
{
mn += vb;
mxchange = min(mxchange, va - vb);
}
else
{
choose ^= 1;
mn += va;
mxchange = min(mxchange, vb - va);
}
}
if(choose != a[1]) mn += mxchange;
cout << mn << " " << mx << "\n";
}
int main()
{
//freopen("sample.in", "r", stdin);
//freopen("sample.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
while(t--) solve();
return 0;
}

浙公网安备 33010602011771号