「2019纪中集训Day7」解题报告

T1、小L的数列

给一个数列 \(\{f_i\}\)

\[f_i = \prod_{j = 1}^{j \leq k} f_{i - j}^{b_j}, \ (i > k) \]

现在给定数列的前 \(k \ (k \le 200)\) 项及 \({b_i}\),求第 \(n\) 项。

\(Sol\)
注意到数列的任意一项 \(f_i \ (i > k)\),都是前 \(k\) 项若干次幂的乘积,只需分别计算每一项对第 \(n\) 项的贡献。
\(dp_i\) 表示第 \(i\) 项的指数,则不难得出:

\[dp_i = \sum_{j = i - k}^{j \leq i - 1} b_{i - j} \times dp_j \]

显然可以用矩阵乘法来优化,因为要枚举前 \(k\) 项,时间复杂度 \(O(k ^ 4 \ \log_2 n)\)
每一项的转移矩阵是一样的,所以可以以 \(O(k ^3 \ \log_2 n)\) 的时间复杂度解决。

代码如下:

#include <cstdio>
#include <cstring>
#include <algorithm>
int in() {
    int x = 0; char c = getchar(); bool f = 0;
    while (c < '0' || c > '9')
        f |= c == '-', c = getchar();
    while (c >= '0' && c <= '9')
        x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
    return f ? -x : x;
}
template<typename T>inline void chk_min(T &_, T __) { _ = _ < __ ? _ : __; }
template<typename T>inline void chk_max(T &_, T __) { _ = _ > __ ? _ : __; }

const int M = 205, mod = 998244353;
int n, m, f[205], y[205], a[205], b[205], res;

struct matrix {
    int a[M][M];
    matrix(int t = 0) {
        memset(a, 0, sizeof(a));
        if (t > 0)
            for (int i = 1; i <= m; ++i)
                a[i][i] = t;
    }
    inline int* operator [] (int x) {
        return a[x];
    }
    inline matrix operator * (matrix &b) const {
        matrix ret;
        for (int k = 1; k <= m; ++k)
            for (int i = 1; i <= m; ++i)
                if (a[i][k])
                    for (int j = 1; j <= m; ++j)
                        if (b[k][j]) {
                            ret[i][j] += 1ll * a[i][k] * b[k][j] % (mod - 1);
                            if (ret[i][j] >= mod - 1)
                                ret[i][j] -= mod - 1;
                        }
        return ret;
    }
} ;

matrix matrix_qpow(matrix base, int b) {
    matrix ret(1);
    for (; b; b >>= 1, base = base * base)
        if (b & 1)
            ret = ret * base;
    return ret;
}
int qpow(int base, int b, int ret = 1) {
    for (; b; b >>= 1, base = 1ll * base * base % mod)
        if (b & 1)
            ret = 1ll * ret * base % mod;
    return ret;
}

int main() {
    //freopen("in", "r", stdin);
    freopen("seq.in", "r", stdin);
    freopen("seq.out", "w", stdout);
    matrix trans;
    n = in(), m = in();
    for (int i = 1; i <= m; ++i)
        y[i] = in();
    for (int i = 1; i <= m; ++i)
        f[i] = in();

    if (n <= m) {
        printf("%d\n", f[n]);
        return 0;
    }

    for (int i = 1; i <= m; ++i)
        trans[i][i - 1] = 1;
    for (int i = 1; i <= m; ++i)
        trans[i][m] = y[m - i + 1];

    res = 1;
    trans = matrix_qpow(trans, n - m);
    for (int i = 1; i <= m; ++i) {
        for (int j = 1; j <= m; ++j)
            b[j] = a[j] = 0;
        b[i] = 1;
        for (int k = 1; k <= m; ++k)
        	for (int j = 1; j <= m; ++j) {
        		a[j] += 1ll * b[k] * trans[k][j] % (mod - 1);
        		if (a[j] >= mod - 1)
        			a[j] -= mod - 1;
        	}
        res = 1ll * res * qpow(f[i], a[m]) % mod;
    }

    printf("%d\n", res);

    return 0;
}

T2、梦境

数轴上给定 \(n\) 条线段 \(\{l_i,r_i\}\)\(m\) 个点 \(\{t_i\}\),每个线段选择一个未匹配且包含于该线段的一个点进行匹配,求最大匹配数。

\(70pts\)
线段树优化建图求二分图最大匹配。

\(100pts\)
\(Sol1\)
点从小到大排序,线段按左端点从小到大排序;
从小到大枚举所有点,把左端点在该点左边的线段放入堆 (右端点为关键字的小根堆) 中;
检查堆顶的右端点是否在当前枚举到的点的右边,若是,将改点与堆顶线段匹配;否则, 重复上述过程,直至条件成立或堆为空。
对于第 \(i\) 个点,只有在它右边的点会被影响,为了使右边的点有更多线段可以选择,所以选右端点最靠左的。
\(Sol2\)
线段按右端点从小到大排序,点放入树状数组;
枚举每一条线段,每条线段选择尽量靠左的点。
\(Sol1\) 一样。

代码如下:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
int in() {
    int x = 0; char c = getchar(); bool f = 0;
    while (c < '0' || c > '9')
        f |= c == '-', c = getchar();
    while (c >= '0' && c <= '9')
        x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
    return f ? -x : x;
}
template<typename T>inline void chk_min(T &_, T __) { _ = _ < __ ? _ : __; }
template<typename T>inline void chk_max(T &_, T __) { _ = _ > __ ? _ : __; }

const int N = 2e5 + 5;
struct node {
    int l, r;
    inline bool operator < (const node &b) const {
        return this->r > b.r;
    }
} a[N];
int n, m, t[N];

inline bool cmp(const node &i, const node &j) {
    return i.l < j.l;
}

int main() {
    //freopen("in", "r", stdin);
    freopen("dream.in", "r", stdin);
    freopen("dream.out", "w", stdout);
    n = in(), m = in();
    for (int i = 1; i <= n; ++i)
        a[i] = (node){in(), in()};
    for (int i = 1; i <= m; ++i)
        t[i] = in();
    std::sort(a + 1, a + 1 + n, cmp);
    std::sort(t + 1, t + 1 + m);
    std::priority_queue <node> q;
    int pos = 1, res = 0;
    for (int i = 1; i <= m; ++i) {
        while (pos <= n && a[pos].l <= t[i]) {
            if (a[pos].r >= t[i])
                q.push(a[pos]);
            ++pos;
        }
        while (!q.empty() && q.top().r < t[i])
            q.pop();
        if (!q.empty() && q.top().r >= t[i])
            ++res, q.pop();
    }
    printf("%d\n", res);
    return 0;
}

T3、树

一棵 \(n\) 个点的树,给定 \(m \ (m \leq 10 ^ 5)\) 个点对(保证两个点不同),求不包含上述点对且点数大于一的简单路径。

\(Sol\)
考虑求至少包含一组点对的路径数量。
把一条路径的端点放在二维平面上,一个点就可以代表一条路径;
对于一个点对 \(u, v\)
若两点有祖先-后代关系(\(u\)\(v\) 的祖先),则可选的路径两端点范围分别为 \(v\) 的子树、除了 \(u\) 包含 \(v\) 的子树的其它所有点。
若两点没有祖先-后代关系,则可选的路径两端点范围分别为 \(u\) 的子树、\(v\)的子树。
\(dfs\) 序放在平面上,变成一个扫描线求矩形覆盖面积的题,线段树维护即可。
线段树的维护:可以 \(push \_ down\) 也可以不用,前者可扩展性更强,由于扫描线删除的线段一定加入过,所以并不用 \(push \_ down\)

代码如下:

//#pragma GCC optimize(2)
#pragma GCC optimize(3, "Ofast", "inline")
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
int in() {
    int x = 0; char c = getchar(); bool f = 0;
    while (c < '0' || c > '9')
        f |= c == '-', c = getchar();
    while (c >= '0' && c <= '9')
        x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
    return f ? -x : x;
}
template<typename T>inline void chk_min(T &_, T __) { _ = _ < __ ? _ : __; }
template<typename T>inline void chk_max(T &_, T __) { _ = _ > __ ? _ : __; }

const int N = 1e5 + 5;

struct edge {
    int next, to;
} e[N << 1];
int ecnt = 1, head[N];

inline void jb(const int u, const int v) {
    e[++ecnt] = (edge){head[u], v}, head[u] = ecnt;
    e[++ecnt] = (edge){head[v], u}, head[v] = ecnt;
}

//heavy-light deposition begin
int siz[N], hson[N], fa[N], dep[N], fro[N], dfn[N];
void dfs_h(const int u) {
    siz[u] = 1;
    for (int i = head[u]; i; i = e[i].next) {
        int v = e[i].to;
        if (v == fa[u])
            continue;
        fa[v] = u, dep[v] = dep[u] + 1;
        dfs_h(v);
        siz[u] += siz[v];
        if (siz[v] > siz[hson[u]])
            hson[u] = v;
    }
}

void dfs_f(const int u, const int tp) {
    dfn[u] = ++dfn[0], fro[u] = tp;
    if (hson[u])
        dfs_f(hson[u], tp);
    for (int i = head[u]; i; i = e[i].next)
        if (e[i].to != fa[u] && e[i].to != hson[u])
            dfs_f(e[i].to, e[i].to);
}
int lca(int u, int v) {
    while (fro[u] != fro[v]) {
        if (dep[fro[u]] > dep[fro[v]])
            std::swap(u, v);
        v = fa[fro[v]];
    }
    return dep[u] < dep[v] ? u : v;
}
int find_son(int u, int v) {
    while (fro[v] != fro[u]) {
        v = fro[v];
        if (fa[v] == u)
            return v;
        v = fa[v];
    }
    return hson[u];
}
//heavy-light deposition end

int n, m;

typedef std::pair<int, int> pii;
std::vector<pii> a[N][2];

struct segment_tree {
    int sum[N << 2], cnt[N << 2];
    void push_up(int p, int tl, int tr) {
        if (cnt[p])
            sum[p] = tr - tl + 1;
        else if (tl == tr)
            sum[p] = 0;
        else
            sum[p] = sum[p << 1] + sum[p << 1 | 1];
    }
    void modify(int l, int r, int k, int tl = 1, int tr = n, int p = 1) {
        if (l <= tl && tr <= r) {
            cnt[p] += k;
            push_up(p, tl, tr);
            return ;
        }
        int mid = (tl + tr) >> 1;
        if (mid >= l)
            modify(l, r, k, tl, mid, p << 1);
        if (mid < r)
            modify(l, r, k, mid + 1, tr, p << 1 | 1);
        push_up(p, tl, tr);
    }
} T;

int main() {
    //freopen("in", "r", stdin);
    freopen("tree.in", "r", stdin);
    freopen("tree.out", "w", stdout);
    n = in(), m = in();
    for (int i = 1; i < n; ++i)
        jb(in(), in());
    dfs_h(3), dfs_f(3, 3);
    for (int i = 1, u, v, w; i <= m; ++i) {
        u = in(), v = in(), w = lca(u, v);
        if (dfn[u] > dfn[v])
            std::swap(u, v);
        if (w == u) {
            w = find_son(u, v);
            a[1][0].push_back(pii(dfn[v], dfn[v] + siz[v] - 1));
            a[dfn[w]][1].push_back(pii(dfn[v], dfn[v] + siz[v] - 1));
            a[dfn[v]][0].push_back(pii(dfn[w] + siz[w], n));
            a[dfn[v] + siz[v]][1].push_back(pii(dfn[w] + siz[w], n));
        } else {
            a[dfn[u]][0].push_back(pii(dfn[v], dfn[v] + siz[v] - 1));
            a[dfn[u] + siz[u]][1].push_back(pii(dfn[v], dfn[v] + siz[v] - 1));
        }
    }

    long long res = 1ll * n * (n - 1) / 2;
    for (int i = 1; i < n; ++i) {
        for (unsigned j = 0; j < a[i][0].size(); ++j)
            T.modify(a[i][0][j].first, a[i][0][j].second, 1);
        for (unsigned j = 0; j < a[i][1].size(); ++j)
            T.modify(a[i][1][j].first, a[i][1][j].second, -1);
        res -= T.sum[1];
    }
    printf("%lld\n", res);
    return 0;
}
posted @ 2019-08-08 16:29  15owzLy1  阅读(160)  评论(1编辑  收藏  举报