AtCoder Beginner Contest 340 A-G
A
按照等差数列直接模拟即可
void solve()
{
int a, b, d;
cin >> a >> b >> d;
for (int i = a; i <= b; i += d)
cout << i << " ";
cout << endl;
}
B
模拟
void solve()
{
int q;
cin >> q;
vector<int> a;
int opt, x;
while (q--)
{
cin >> opt >> x;
if (opt == 1)
a.push_back(x);
else
{
cout << a[a.size() - x] << endl;
}
}
}
C
如果直接模拟肯定会超时,考虑用set维护种类,map维护数量,这样可以保证每种数只会被操作一次
void solve()
{
ll n;
cin >> n;
map<ll, ll> mp;
mp[n] = 1;
set<ll> s;
s.insert(n);
ll res = 0;
while (s.size() && *s.rbegin() >= 2)
{
ll t = *s.rbegin();
ll num = mp[t];
s.erase(*s.rbegin());
res += t * num;
if (t % 2 == 0)
{
s.insert(t / 2);
mp[t / 2] += num * 2;
}
else
{
ll lo = t / 2, hi = (t - 1) / 2 + 1;
if (lo >= 2)
s.insert(lo), mp[lo] += num;
if (hi >= 2)
s.insert(hi), mp[hi] += num;
}
}
cout << res << endl;
}
D
dijkstra板子
void solve()
{
int n;
cin >> n;
vector<vector<pll>> e(n + 1);
for (int i = 1; i < n; i++)
{
int a, b, x;
cin >> a >> b >> x;
e[i].push_back({i + 1, a});
e[i].push_back({x, b});
}
auto dij = [&](int s) -> ll
{
vector<ll> dist(n + 1, 1e18);
vector<bool> st(n + 1, 0);
priority_queue<pll, vector<pll>, greater<pll>> q;
dist[s] = 0;
q.push({0, s});
while (q.size())
{
auto t = q.top();
q.pop();
if (st[t.y])
continue;
st[t.y] = true;
for (auto [to, w] : e[t.y])
if (dist[t.y] + w < dist[to])
dist[to] = dist[t.y] + w, q.push({dist[to], to});
}
return dist[n];
};
cout << dij(1) << endl;
}
E
模拟,用树状数组维护差分数组,就可以实现区间加,单点查询
struct BIT
{
int n;
vector<ll> a;
BIT(int _n) : n(_n + 3), a(n + 1) {}
int lb(int x) { return x & -x; }
void build(int n, vector<ll> &s)
{
for (int i = 1; i <= n; i++)
{
a[i] += s[i];
int fa = i + lb(i);
if (fa <= n)
a[fa] += a[i];
}
}
void add(int x, ll y)
{
for (; x < n; x += lb(x))
a[x] += y;
}
ll query(int x)
{
ll res = 0;
for (; x; x ^= lb(x))
res += a[x];
return res;
}
};
void solve()
{
int n, m;
cin >> n >> m;
vector<ll> a(n + 1), b(m + 1);
for (int i = 1; i <= n; i++)
cin >> a[i];
for (int i = 1; i <= m; i++)
cin >> b[i];
BIT bit(n + 10);
for (int i = 1; i <= n; i++)
bit.add(i, a[i]), bit.add(i + 1, -a[i]);
for (int i = 1; i <= m; i++)
{
int u = b[i] + 1;
ll num = bit.query(u);
bit.add(u, -num), bit.add(u + 1, num);
ll add = num / n;
bit.add(1, add), bit.add(n + 1, -add);
num %= n;
if (u + num <= n)
bit.add(u + 1, 1), bit.add(u + num + 1, -1);
else
{
bit.add(u + 1, 1), bit.add(n + 1, -1);
num -= n - u;
bit.add(1, 1), bit.add(num + 1, -1);
}
}
for (int j = 1; j <= n; j++)
cout << bit.query(j) << " ";
cout << endl;
}
F
考虑用向量来计算面积,类似22桂林的E题,设未知点为\((ax,ay)\),那么可以得到:
\(ax*y-ay*x=2\)
用exgcd计算即可
```cpp
ll exgcd(ll a, ll b, ll &x, ll &y)
{
if (!b)
{
x = 1, y = 0;
return a;
}
ll d = exgcd(b, a % b, y, x);
y -= (a / b) * x;
return d;
}
void solve()
{
ll x, y;
cin >> x >> y;
pll t = {x, y};
ll ansx, ansy;
ll g = exgcd(-t.y, t.x, ansx, ansy);
if (2 % g)
cout << "-1" << endl;
else
{
ll mul = 2 / g;
cout << ansx * mul << " " << ansy * mul << endl;
}
}
G
1.虚树dp
存一下同种颜色的点,对每种颜色建虚树
在虚树上dp答案
dp[u]表示以u为根的树,所有叶子节点是同种颜色(设为k)的方案数
$dp_u= {\textstyle \prod_{to \in son(i)} (f[to]+1)} $加一是因为可以不选
但是这样是不对的,因为如果当前点u的颜色与k不同:
1.它自己一个点肯定是不对的,要减去一(87,88行)
2.u和某一个儿子构成的树也是不对的,这样u成叶子了,要减去\(f[to]\)(90,91)
void solve()
{
ll n, tot = 0, ans = 0;
cin >> n;
vector<int> a(n + 1), dfn(n + 1, 1), dist(n + 1, 0);
vector<vector<ll>> to(n + 1, vector<ll>(20, 0));
vector<vector<int>> e(n + 1), col(n + 1), ne(n + 1);
vector<ll> f(n + 1, 0);
vector<int> c;
for (int i = 1; i <= n; i++)
cin >> a[i], col[a[i]].push_back(i);
for (int i = 1; i < n; i++)
{
int u, v;
cin >> u >> v;
e[u].push_back(v), e[v].push_back(u);
}
auto dfs = [&](auto dfs, int u, int fa) -> void
{
dfn[u] = ++tot;
to[u][0] = fa;
for (auto to : e[u])
if (to != fa)
{
dist[to] = dist[u] + 1;
dfs(dfs, to, u);
}
};
dfs(dfs, 1, 0);
for (int j = 1; j < 20; j++)
for (int i = 1; i <= n; i++)
{
if (to[i][j - 1])
to[i][j] = to[to[i][j - 1]][j - 1];
}
auto lca = [&](int u, int v) -> int
{
if (dist[u] < dist[v])
swap(u, v);
int cha = dist[u] - dist[v];
for (int j = 0; j < 20 && cha; j++, cha /= 2)
if (cha & 1)
u = to[u][j];
if (u != v)
{
for (int j = 19; j >= 0; j--)
if (to[u][j] != to[v][j])
u = to[u][j], v = to[v][j];
return to[u][0];
}
return u;
};
auto build = [&](int i) -> void
{
sort(col[i].begin(), col[i].end(), [&](int a, int b)
{ return dfn[a] < dfn[b]; });
c.push_back(1);
for (int j = 0; j + 1 < col[i].size(); j++)
{
c.push_back(col[i][j]);
c.push_back(lca(col[i][j], col[i][j + 1]));
}
c.push_back(col[i].back());
sort(c.begin(), c.end(), [&](int a, int b)
{ return dfn[a] < dfn[b]; });
c.erase(unique(c.begin(), c.end()), c.end());
for (int i = 0; i + 1 < c.size(); i++)
{
ll lc = lca(c[i], c[i + 1]);
ne[lc].push_back(c[i + 1]);
ne[c[i + 1]].push_back(lc);
}
};
auto dp = [&](auto dp, int u, int fa, int k) -> void
{
f[u] = 1;
for (auto to : ne[u])
if (to != fa)
{
dp(dp, to, u, k);
f[u] = f[u] * (f[to] + 1) % mod;
}
ll res = f[u];
if (a[u] != k)
{
res = (res - 1 + mod) % mod;
f[u] = (f[u] - 1 + mod) % mod;
for (auto to : ne[u])
if (to != fa)
res = (res - f[to] + mod) % mod;
}
ans = (ans + res) % mod;
};
for (int i = 1; i <= n; i++)
if (col[i].size())
{
build(i);
dp(dp, 1, 0, i);
for (auto t : c)
f[t] = 0, ne[t].clear();
c.clear();
}
cout << ans << endl;
}
2.启发式合并
void solve()
{
ll ans = 0;
int n;
cin >> n;
vector<int> a(n + 1);
vector<vector<int>> e(n + 1);
for (int i = 1; i <= n; i++)
cin >> a[i];
for (int i = 1; i < n; i++)
{
int u, v;
cin >> u >> v;
e[u].push_back(v), e[v].push_back(u);
}
auto dfs = [&](auto dfs, int u, int fa) -> map<ll, ll>
{
map<ll, ll> fmp;
for (auto to : e[u])
if (to != fa)
{
auto smp = dfs(dfs, to, u);
ans = (ans + smp[a[u]]) % mod; // u 与to 组成树
if (fmp.size() < smp.size())
swap(fmp, smp);
for (auto [col, sz] : smp)
{
ans = (ans + fmp[col] * sz % mod) % mod;
fmp[col] = ((fmp[col] + 1) * (sz + 1) % mod - 1 + mod) % mod;
}
}
fmp[a[u]] = (fmp[a[u]] + 1) % mod;
ans = (ans + 1) % mod; // u自己作为树
return fmp;
};
dfs(dfs, 1, 0);
cout << ans << endl;
}