最近懒死了,所以没有更新太多博客。在这里做一个小的集合吧。
「雅礼集训 2017 Day8」共
这题好屌。如果我们要搞一颗这样的树,使得 \(k\) 个点的深度都为奇数,那么我们完全可以放到二分图上去做:二分图中不存在奇环,所以我们可以把深度奇、偶的点放在两部,做一遍这个图的生成树计数即可。那么,「完全二分图」的生成树计数结论是 \(n^{m – 1}m^{n – 1}\),证明:BZOJ4766: 文艺计算姬(Prufer序列) 题
「雅礼集训 2017 Day8」爷
看题解的时候被震惊到:还真特么是分块题。考虑在 DFS 序上做分块,然后标记维护。对于询问,我们可以二分答案来确定第 \(k\) 小;对于修改,就用分块的基本操作即可。对于查找区间内有多少个数小于等于 \(mid\),如果用 lower_bound,时间复杂度会来到 \(\Theta(\sqrt{n}n\log n \log \sqrt{n}\),会超时(比赛的时候我写的是这种,亲测有 70 分)。我们发现 \(len\) 很小,所以我们可以直接开个数组记录,并且定期重构。
「雅礼集训 2017 Day5」珠宝
一道很棒的背包+决策单调性题。单调性:我们发现这个 \(c_i\) 很小,所以我们可以把同代价的物品按价值从大到小排序,然后发现越往后选所带来的价值越小。解决这个问题之后,我们可以把转移进行分类:在模域 \(c_i\) 下,枚举取模后的价值 \(w\),然后再枚举源位置,也就是使得 \(w’ \bmod c_i \equiv w\) 的所有 \(w’\)。这个过程比较调和级数。具体看代码,我觉得这个模域分类进行批量转移的操作很屌。
int n, k, max_price, pos[MAX_K], ptot;
x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
void solve(int idx, int l, int r, int LB, int RB)
int mid = (LB + RB) >> 1, gpt = l;
for (int i = max(l, int(mid - store[idx].size())); i <= min(mid, r); i++)
if (dp[idx][pos[mid]] < dp[idx - 1][pos[i]] + (i == mid ? 0 : store[idx][mid - i - 1]))
gpt = i, dp[idx][pos[mid]] = dp[idx - 1][pos[i]] + (i == mid ? 0 : store[idx][mid - i - 1]);
solve(idx, l, gpt, LB, mid - 1), solve(idx, gpt, r, mid + 1, RB);
for (int i = 1, c, v; i <= n; i++)
c = read(), v = read(), store[c].push_back(v), max_price = max(max_price, c);
for (int i = 1; i <= max_price; i++)
sort(store[i].begin(), store[i].end());
reverse(store[i].begin(), store[i].end());
for (int j = 1, siz = store[i].size(); j < siz; j++)
store[i][j] += store[i][j - 1];
for (int price = 1; price <= max_price; price++)
for (int start_pos = 0; start_pos < price; start_pos++)
for (int i = start_pos; i <= k; i += price)
solve(price, 1, ptot, 1, ptot);
for (int i = 1; i <= k; i++)
printf("%lld ", dp[max_price][i]);
// B.cpp
#include <bits/stdc++.h>
using namespace std;
const int MAX_K = 50010;
typedef long long ll;
int n, k, max_price, pos[MAX_K], ptot;
vector<ll> store[330];
ll dp[330][MAX_K];
int read()
{
int x = 0, f = 1;
char ch = getchar();
while (!isdigit(ch))
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (isdigit(ch))
x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
return x * f;
}
void solve(int idx, int l, int r, int LB, int RB)
{
if (LB > RB)
return;
int mid = (LB + RB) >> 1, gpt = l;
for (int i = max(l, int(mid - store[idx].size())); i <= min(mid, r); i++)
if (dp[idx][pos[mid]] < dp[idx - 1][pos[i]] + (i == mid ? 0 : store[idx][mid - i - 1]))
gpt = i, dp[idx][pos[mid]] = dp[idx - 1][pos[i]] + (i == mid ? 0 : store[idx][mid - i - 1]);
solve(idx, l, gpt, LB, mid - 1), solve(idx, gpt, r, mid + 1, RB);
}
int main()
{
n = read(), k = read();
for (int i = 1, c, v; i <= n; i++)
c = read(), v = read(), store[c].push_back(v), max_price = max(max_price, c);
for (int i = 1; i <= max_price; i++)
{
sort(store[i].begin(), store[i].end());
reverse(store[i].begin(), store[i].end());
for (int j = 1, siz = store[i].size(); j < siz; j++)
store[i][j] += store[i][j - 1];
}
for (int price = 1; price <= max_price; price++)
for (int start_pos = 0; start_pos < price; start_pos++)
{
ptot = 0;
for (int i = start_pos; i <= k; i += price)
pos[++ptot] = i;
solve(price, 1, ptot, 1, ptot);
}
for (int i = 1; i <= k; i++)
printf("%lld ", dp[max_price][i]);
putchar('\n');
return 0;
}
// B.cpp
#include <bits/stdc++.h>
using namespace std;
const int MAX_K = 50010;
typedef long long ll;
int n, k, max_price, pos[MAX_K], ptot;
vector<ll> store[330];
ll dp[330][MAX_K];
int read()
{
int x = 0, f = 1;
char ch = getchar();
while (!isdigit(ch))
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (isdigit(ch))
x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
return x * f;
}
void solve(int idx, int l, int r, int LB, int RB)
{
if (LB > RB)
return;
int mid = (LB + RB) >> 1, gpt = l;
for (int i = max(l, int(mid - store[idx].size())); i <= min(mid, r); i++)
if (dp[idx][pos[mid]] < dp[idx - 1][pos[i]] + (i == mid ? 0 : store[idx][mid - i - 1]))
gpt = i, dp[idx][pos[mid]] = dp[idx - 1][pos[i]] + (i == mid ? 0 : store[idx][mid - i - 1]);
solve(idx, l, gpt, LB, mid - 1), solve(idx, gpt, r, mid + 1, RB);
}
int main()
{
n = read(), k = read();
for (int i = 1, c, v; i <= n; i++)
c = read(), v = read(), store[c].push_back(v), max_price = max(max_price, c);
for (int i = 1; i <= max_price; i++)
{
sort(store[i].begin(), store[i].end());
reverse(store[i].begin(), store[i].end());
for (int j = 1, siz = store[i].size(); j < siz; j++)
store[i][j] += store[i][j - 1];
}
for (int price = 1; price <= max_price; price++)
for (int start_pos = 0; start_pos < price; start_pos++)
{
ptot = 0;
for (int i = start_pos; i <= k; i += price)
pos[++ptot] = i;
solve(price, 1, ptot, 1, ptot);
}
for (int i = 1; i <= k; i++)
printf("%lld ", dp[max_price][i]);
putchar('\n');
return 0;
}
[CERC2017]Gambling Guide
这题的转移模型比较经典,用最短路算法进行转移的方式。我们设置 \(dp[u]\) 为从点 \(x\) 到点 \(n\) 的期望硬币消耗数。那么,转移:
\[ dp[u] = \sum_{v \in nearby} \frac{\min\{dp[u], dp[v]\}}{deg[u]} + 1 \]
我们可以一开始把最小值设置为自身,然后用 Dijsktra 搞一下转移即可。我们需要记录被转移和、被转移次数,然后进行自身的计算。
const int MAX_N = 301000;
typedef pair<double, int> pdi;
int head[MAX_N], n, m, current, deg[MAX_N], cnt[MAX_N];
double dist[MAX_N], sum[MAX_N];
void addpath(int src, int dst)
edges[current].to = dst, edges[current].nxt = head[src];
pq.push(make_pair(0, n));
for (int i = head[u]; i != -1; i = edges[i].nxt)
if (vis[edges[i].to] == false)
cnt[edges[i].to]++, sum[edges[i].to] += dist[u];
dist[edges[i].to] = double(sum[edges[i].to] + deg[edges[i].to]) / double(cnt[edges[i].to]);
pq.push(make_pair(-dist[edges[i].to], edges[i].to));
memset(head, -1, sizeof(head));
for (int i = 1, u, v; i <= m; i++)
scanf("%d%d", &u, &v), addpath(u, v), addpath(v, u), deg[u]++, deg[v]++;
shortest_path(), printf("%.10lf\n", dist[1]);
// P4745.cpp
#include <bits/stdc++.h>
using namespace std;
const int MAX_N = 301000;
typedef pair<double, int> pdi;
int head[MAX_N], n, m, current, deg[MAX_N], cnt[MAX_N];
double dist[MAX_N], sum[MAX_N];
bool vis[MAX_N];
struct edge
{
int to, nxt;
} edges[MAX_N << 1];
void addpath(int src, int dst)
{
edges[current].to = dst, edges[current].nxt = head[src];
head[src] = current++;
}
void shortest_path()
{
priority_queue<pdi> pq;
pq.push(make_pair(0, n));
while (!pq.empty())
{
int u = pq.top().second;
pq.pop();
if (vis[u])
continue;
vis[u] = true;
for (int i = head[u]; i != -1; i = edges[i].nxt)
if (vis[edges[i].to] == false)
{
cnt[edges[i].to]++, sum[edges[i].to] += dist[u];
dist[edges[i].to] = double(sum[edges[i].to] + deg[edges[i].to]) / double(cnt[edges[i].to]);
pq.push(make_pair(-dist[edges[i].to], edges[i].to));
}
}
}
int main()
{
memset(head, -1, sizeof(head));
scanf("%d%d", &n, &m);
for (int i = 1, u, v; i <= m; i++)
scanf("%d%d", &u, &v), addpath(u, v), addpath(v, u), deg[u]++, deg[v]++;
shortest_path(), printf("%.10lf\n", dist[1]);
return 0;
}
// P4745.cpp
#include <bits/stdc++.h>
using namespace std;
const int MAX_N = 301000;
typedef pair<double, int> pdi;
int head[MAX_N], n, m, current, deg[MAX_N], cnt[MAX_N];
double dist[MAX_N], sum[MAX_N];
bool vis[MAX_N];
struct edge
{
int to, nxt;
} edges[MAX_N << 1];
void addpath(int src, int dst)
{
edges[current].to = dst, edges[current].nxt = head[src];
head[src] = current++;
}
void shortest_path()
{
priority_queue<pdi> pq;
pq.push(make_pair(0, n));
while (!pq.empty())
{
int u = pq.top().second;
pq.pop();
if (vis[u])
continue;
vis[u] = true;
for (int i = head[u]; i != -1; i = edges[i].nxt)
if (vis[edges[i].to] == false)
{
cnt[edges[i].to]++, sum[edges[i].to] += dist[u];
dist[edges[i].to] = double(sum[edges[i].to] + deg[edges[i].to]) / double(cnt[edges[i].to]);
pq.push(make_pair(-dist[edges[i].to], edges[i].to));
}
}
}
int main()
{
memset(head, -1, sizeof(head));
scanf("%d%d", &n, &m);
for (int i = 1, u, v; i <= m; i++)
scanf("%d%d", &u, &v), addpath(u, v), addpath(v, u), deg[u]++, deg[v]++;
shortest_path(), printf("%.10lf\n", dist[1]);
return 0;
}
CF914G Sum the Fibonacci
先看下条件:
- \((s_a | s_b) \& s_c \& (s_d \oplus s_e) = 2^i, s_a \& s_b = 0\)
首先,\(s_a | s_b, s_a \& s_b = 0\) 就是子集卷积,用 FWT_OR 进行展开,\(\Theta(n \log^2 n\) 做完;\(s_c\)不用考虑;\(s_d \oplus s_e\) 用 FWT_XOR 搞一下就行了。接下来这三个部分用 FWT_AND 做就完事了。