Loading [MathJax]/extensions/tex2jax.js

「杂题集」一月下旬

最近懒死了,所以没有更新太多博客。在这里做一个小的集合吧。

「雅礼集训 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’\)。这个过程比较调和级数。具体看代码,我觉得这个模域分类进行批量转移的操作很屌。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// 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; }
// 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 搞一下转移即可。我们需要记录被转移和、被转移次数,然后进行自身的计算。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// 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; }
// 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 做就完事了。

Leave a Reply

Your email address will not be published. Required fields are marked *