5.28 题解

A

转化成 $\sum i^{\sum\limits_{j=1}^n[\text{sum}(j)=i]}$。

枚举 $i$,数位 DP 算 $\sum\limits_{j=1}^n[\text{sum}(j)=i]$ 即可。

#include <cstdio>
#include <cstring>
#define M 10000007
int c, k, a[150];long long n, q = 1, f[150][150];
long long F(int x, int y, bool l)
{
    if(!x) return y == k;
    if(!l && ~f[x][y]) return f[x][y];long long q = 0;
    for(int i = 0, j = l ? a[x] : 1;i <= j;++i)
        q += F(x - 1, y + i, l && i == j);
    if(!l) f[x][y] = q;return q;
}
long long G(long long n)
{
    memset(f, -1, sizeof f);c = 0;
    while(n) a[++c] = n & 1, n >>= 1;
    return F(c, 0, 1);
}
long long P(long long x, long long y)
{
    long long q = 1;
    for(;y;y >>= 1, x = x * x % M)
        if(y & 1) q = q * x % M;
    return q;
}
int main()
{
//  freopen("a.in", "r", stdin);
//  freopen("a.out", "w", stdout);
    scanf("%lld", &n);
    for(k = 1;k <= 65;++k)
        q = q * P(k, G(n)) % M;
    printf("%lld", q);
    fclose(stdin);fclose(stdout);
    return 0;
}

B

不会。

C

整体二分套二维树状数组。

令询问 $(x1,y1,x2,y2,k,s,t)$ 表示 $(x1,y1)$ 到 $(x2,y2)$ 在 $[s,t]$ 内的第 $k$ 小。

考虑对某组 $s,t$ 处理所有询问 $(x1,y1,x2,y2,k,s,t)$,设 $m=\lfloor\dfrac{s+t}2\rfloor$。

$\forall a_{i,j}\in[s,m]$,令 $c_{i,j}\gets c_{i,j}+1$。然后对询问 $(x1,y1,x2,y2,k,s,t)$,

若 $\sum\limits_{i=x1}^{x2}\sum\limits_{j=y1}^{y2} c_{i,j}\ge k$ 则其答案等于 $(x1,y1,x2,y2,k,s,m)$ 的答案,

否则其答案等于 $(x1,y1,x2,y2,k-\sum\limits_{i=x1}^{x2}\sum\limits_{j=y1}^{y2} c_{i,j},m+1,t)$ 的答案。

还原 $c$ 数组,递归处理所有 $(x1,y1,x2,y2,k,s,m)$ 的询问和 $(x1,y1,x2,y2,k,m+1,t)$ 的询问。

二维树状数组维护 $c$。

#include <cstdio>
struct S{int o, l1, l2, r1, r2, k, i;}a[500050], q1[500050], q2[500050];
int n, q, m, z[500050], c[550][550];
void M(int x, int y, int k)
{
    for(;x <= n;x += x & -x)
        for(int i = y;i <= n;i += i & -i)
            c[x][i] += k;
}
int Q(int x, int y)
{
    int q = 0;
    for(;x;x &= x - 1)
        for(int i = y;i;i &= i - 1)
            q += c[x][i];
    return q;
}
void D(int l, int r, int s, int t)
{
    if(l > r) return;
    if(s == t)
    {
        for(int i = l;i <= r;++i)
            if(a[i].o == 2) z[a[i].i] = s;
        return;
    }
    int m = s + t >> 1, c1 = 0, c2 = 0;
    for(int i = l, o;i <= r;++i)
    {
        if(a[i].o & 1)
        {
            if(a[i].k <= m)
                M(a[i].l1, a[i].l2, 1), q1[++c1] = a[i];
            else
                q2[++c2] = a[i];
        }
        else
        {
            o = Q(a[i].r1, a[i].r2) - Q(a[i].l1 - 1, a[i].r2) - Q(a[i].r1, a[i].l2 - 1) + Q(a[i].l1 - 1, a[i].l2 - 1);
            if(o >= a[i].k)
                q1[++c1] = a[i];
            else
                a[i].k -= o, q2[++c2] = a[i];
        }
    }
    for(int i = 1;i <= c1;++i) if(q1[i].o & 1) M(q1[i].l1, q1[i].l2, -1);
    for(int i = 1;i <= c1;++i) a[l + i - 1] = q1[i];
    for(int i = 1;i <= c2;++i) a[l + c1 + i - 1] = q2[i];
    D(l, l + c1 - 1, s, m);D(l + c1, r, m + 1, t);
}
int main()
{
//  freopen("c.in", "r", stdin);
//  freopen("c.out", "w", stdout);
    scanf("%d%d", &n, &q);
    for(int i = 1;i <= n;++i)
        for(int j = 1;j <= n;++j)
            a[++m] = (S){1, i, j, 0, 0, 0, 0}, scanf("%d", &a[m].k);
    for(int i = 1;i <= q;++i)
        a[++m] = (S){2, 0, 0, 0, 0, 0, i}, scanf("%d%d%d%d%d", &a[m].l1, &a[m].l2, &a[m].r1, &a[m].r2, &a[m].k);
    D(1, m, 1, 1e9);
    for(int i = 1;i <= q;++i)
        printf("%d\n", z[i]);
    return 0;
}

D

虚树 DP。

回收路径树就是最短路树,直接建出来。

具体地,以 $K$ 为源跑 Dijkstra,然后每个点向其最短路上的前驱连边。

设 $t_x$ 表示 $x$ 是否投放区域,修改操作直接取反。

被标记的区域就是以回收区域为关键点集建的虚树,每次询问直接建出来。

具体地,把关键点集按 DFS 序排序,然后关键点集与其相邻每两项的 LCA 即为标记区域,

标记区域之间按原树祖先关系连边,边权为原树上两点间边权和。

在建出的虚树上跑 DP。设 $f_x$ 表示阻碍 $x$ 子树内所有投放区域的代价,则有

$$ f_u=\sum\limits_{v\in son_u}\begin{cases}w(u,v)&t_v=1\\\min(w(u,v),f_v)&t_v=0\end{cases} $$

#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define P pair<int, int>
using namespace std;
const int N = 1e9;
struct G
{
    struct E{int v, w, t;}e[200050];int c, h[50050];
    void A(int u, int v, int w) {e[++c] = (E){v, w, h[u]};h[u] = c;}
}A, T, V;
bool t[50050], e[50050];P r[50050];
int n, m, s, p, O, a[50050], z[50050], b[50050], d[50050], f[50050][20], g[50050], W[50050], o[50050];
priority_queue<P, vector<P>, greater<P> > q;
bool C(int x, int y) {return b[x] < b[y];}
void D(int u)
{
    b[u] = ++p;
    for(int i = T.h[u], v;i;i = T.e[i].t)
    {
        d[v = T.e[i].v] = d[u] + 1;
        W[v] = W[f[v][0] = u] + T.e[i].w;
        for(int j = 1;f[v][j - 1];++j)
            f[v][j] = f[f[v][j - 1]][j - 1];D(v);
    }
}
int L(int x, int y)
{
    if(d[x] < d[y]) swap(x, y);
    while(d[x] > d[y]) x = f[x][__lg(d[x] - d[y])];
    if(x == y) return x;
    for(int k = __lg(d[x]);k >= 0;--k)
        if(f[x][k] != f[y][k]) x = f[x][k], y = f[y][k];
    return f[x][0];
}
void F(int u)
{
    o[u] = 0;
    for(int i = V.h[u], v;i;i = V.e[i].t)
        F(v = V.e[i].v), o[u] += min(V.e[i].w, t[v] ? N : o[v]);
}
int main()
{
//  freopen("d.in", "r", stdin);
//  freopen("d.out", "w", stdout);
    scanf("%d%d%d%d", &n, &m, &s, &O);
    for(int i = 0, u, v, w;i < m;++i)
        scanf("%d%d%d", &u, &v, &w), A.A(u, v, w), A.A(v, u, w);
    memset(g, 0x3f, sizeof g);q.push(P(g[s] = 0, s));
    while(!q.empty())
    {
        int u = q.top().second;q.pop();
        if(!e[u])
        {
            e[u] = 1;
            for(int i = A.h[u], v;i;i = A.e[i].t)
                if(g[v = A.e[i].v] > g[u] + A.e[i].w || g[v] == g[u] + A.e[i].w && r[v].first > u)
                    q.push(P(g[v] = g[r[v].first = u] + A.e[i].w, v)), r[v].second = A.e[i].w;
        }
    }
    for(int i = 1;i <= n;++i) if(r[i].first) T.A(r[i].first, i, r[i].second);D(s);
    for(int i = 0, r, k, c = 0;i < O;++i)
    {
        scanf("%d%d", &r, &k);
        if(r)
        {
            for(int j = 0;j < c;++j) V.h[z[j]] = 0;V.c = c = 0;
            for(int j = 0;j < k;++j) scanf("%d", a + j);a[k++] = s;sort(a, a + k, C);z[c++] = a[0];
            for(int j = 1;j < k;++j) z[c++] = L(a[j], a[j - 1]), z[c++] = a[j];sort(z, z + c, C);c = unique(z, z + c) - z;
            for(int j = 1, l;j < c;++j) l = L(z[j], z[j - 1]), V.A(l, z[j], W[z[j]] - W[l]);bool U = 1;
            for(int j = 0;j < c;++j) if(t[z[j]]) {U = 0;break;}if(U) {puts("-1");continue;}
            F(z[0]);printf("%d\n", o[z[0]]);
        }
        else
        {
            for(int j = 0, x;j < k;++j)
                scanf("%d", &x), t[x] ^= 1;
        }
    }
    return 0;
}

无返回值函数必须是 void 型的

posted @ 2023-05-28 18:32  Jijidawang  阅读(7)  评论(0)    收藏  举报  来源