「杂题集」一月下旬

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

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

// 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 搞一下转移即可。我们需要记录被转移和、被转移次数,然后进行自身的计算。

// 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 *