BS8388 礼物gift

发布时间 2023-07-06 07:26:30作者: 牛肉爱吃dks

BS8388 礼物gift

题目传送门

Problem

有一棵 \(n\)\(1\le n\le3\times10^5\))个点的树,由儿子指向父亲的单向边组成。每个点有一种颜色 \(color_i\)\(color_i\le m\le10^3\))。

\(q\)\(0\le q\le5\times10^4\)) 个询问,每个询问给定 \(c\)\(2\le C\le 5\))个点。

令这 \(c\) 个点的 LCA 为 \(p\),要求对于 \(c\) 个点中每个点,在它到 \(p\) 的链上选择相同的颜色种数,且每种颜色最多被选一次。

问最多有多少种颜色被选。

Solution

首先我们想要求出询问的 \(c\) 条链上的颜色集合。将问题从树上剥离下来。

考虑重链剖分,用线段树维护区间 \(bitset\) 并集。预处理每个点到所在重链链顶路径的颜色集合,询问时只需对最上面的一段在线段树上询问。时间复杂度 \(O(\frac{nm}{\omega}+\frac{qcm\log n}{\omega})\)

我们发现这个问题是可以网络流跑的——从每个点向对应链上包含的颜色连边,源点向每个点连一条特定容量的边,颜色向汇点连边,跑最大流,满流则可行。容量可以二分,但这样复杂度是 \(O(maxflow(n+m,nm)\log m)\) 的,会 TLE。

换一种思路,我们可以发现,上面我们建的是一个二分图,设每个点选 \(x\) 种颜色。相当于是将每个点,拆成 \(x\) 个点,判断是否能完美匹配。考虑用霍尔定理。

Hall 定理(霍尔定理):对于一个二分图,设两侧点集分别为 \(X\)\(Y\)\(X\) 存在完美匹配当且仅当对于 \(X\) 中任意 \(k\) 个点与 \(Y\) 中至少 \(k\) 个点相邻。

现在求出了 \(c\) 条链上的颜色集合。\(O(\frac{qm2^c}{\omega})\) 求出 \(dp_S\) 表示集合 \(S\) 中的链的颜色集合的并。那么根据霍尔定理,答案为 \(\min\{\lfloor\frac{dp_S}{|S|}\rfloor\}\)

总时间复杂度:\(O((n+qc\log n+q2^c)\frac{m}{\omega})\)

#include <bits/stdc++.h>
namespace _default{
    using namespace std;
    #define lovely return
    #define _lzy_ 0
    #define LL long long
    #define DB double
    #define PII std::pair<int, int>
    #define X first
    #define Y second
    #define uF(i, x, y) for(int i = (x); i <= (y); ++ i)
    #define uf(i, x, y) for(int i = (x); i < (y); ++ i)
    #define dF(i, x, y) for(int i = (x); i >= (y); -- i)
    #define df(i, x, y) for(int i = (x); i > (y); -- i)
    #define ef(i, u) for(int i = head[(u)]; i; i = ne[i])
    #define sett(x, y) memset(x, y, sizeof x)
    #define copy(x, y) memcpy(x, y, sizeof x)
    const int MOD = 1e9 + 7, INF = 0x3f3f3f3f;
    const DB EPS = 1e-8;
    template<typename T> T read(){
        T s = 0; int f = 0; char ch = getchar();
        while(!isdigit(ch)){if(ch == '-') f = 1; ch = getchar();}
        while(isdigit(ch)) s = s * 10 + ch - 48, ch = getchar();
        return f ? ~s + 1 : s;
    }
    template<typename T> void write(T x, const char *s = ""){
        static int st[40]; int top = 0;
        if(x < 0){putchar('-'); x = -x;}
        if(!x) putchar('0');
        while(x) st[++ top] = x % 10, x /= 10;
        while(top) putchar(st[top --] + 48);
        printf("%s", s);
    }
    template<typename T> void updmin(T &x, T y){if(x > y) x = y;}
    template<typename T> void updmax(T &x, T y){if(x < y) x = y;}
    template<typename T> void updadd(T &x, T y){(x += y % MOD) %= MOD;}
    template<typename T> void updmul(T &x, T y){(x *= y % MOD) %= MOD;}
    int cmp(DB a, DB b){if(fabs(a - b) < EPS) return 0; return a - b > EPS ? 1 : -1;}
}
using namespace _default;
#define BS bitset<1005>
const int N = 3e5 + 5;
int n, m, q;
int co[N];
BS sum[N];
int head[N], to[N], ne[N], idx;
void Add_Edge(int u, int v){to[++ idx] = v, ne[idx] = head[u], head[u] = idx;}
int fa[N], dpt[N], son[N], sz[N];
void DFS1(int u, int fat){
    dpt[u] = dpt[fat] + 1, sz[u] = 1, sum[u][co[u]] = 1;
    ef(i, u){
        int v = to[i]; DFS1(v, u);
        sz[u] += sz[v];
        if(sz[v] > sz[son[u]]) son[u] = v;
    }
}
int dfn[N], id[N], top[N], ts;
void DFS2(int u, int t){
    id[dfn[u] = ++ ts] = u, top[u] = t;
    if(u != t) sum[u] |= sum[fa[u]];
    if(son[u]) DFS2(son[u], t);
    ef(i, u) if(to[i] != son[u]) DFS2(to[i], to[i]);
}
int LCA(int u, int v){
    while(top[u] != top[v]){
        if(dpt[top[u]] < dpt[top[v]]) swap(u, v);
        u = fa[top[u]];
    }
    return dpt[u] < dpt[v] ? u : v;
}
struct Node{
    int l, r;
    BS sum;
    Node(){l = r = 0; sum.reset();}
}tr[N << 2];
void Pushup(int p){tr[p].sum = tr[p << 1].sum | tr[p << 1 | 1].sum;}
void Build(int l, int r, int p){
    tr[p].l = l, tr[p].r = r;
    if(l == r){tr[p].sum[co[id[l]]] = 1; return;}
    int mid = l + r >> 1;
    Build(l, mid, p << 1);
    Build(mid + 1, r, p << 1 | 1);
    Pushup(p);
}
BS Section_Query_sum(int l, int r, int p){
    if(l <= tr[p].l && tr[p].r <= r) return tr[p].sum;
    int mid = tr[p].l + tr[p].r >> 1; BS res; res.reset();
    if(l <= mid) res |= Section_Query_sum(l, r, p << 1);
    if(r > mid) res |= Section_Query_sum(l, r, p << 1 | 1);
    return res;
}
BS Query(int u, int v){
    BS res; res.reset();
    while(top[u] != top[v]) res |= sum[u], u = fa[top[u]];
    res |= Section_Query_sum(dfn[v], dfn[u], 1);
    return res;
}
int p[10];
BS pc[10], dp[1 << 5 | 5];
signed main(){
    n = read<int>(), m = read<int>(), q = read<int>();
    if(!q) return 0;
    uF(i, 2, n){fa[i] = read<int>(); Add_Edge(fa[i], i);}
    uF(i, 1, n) co[i] = read<int>();
    DFS1(1, 0);
    DFS2(1, 1);
    Build(1, n, 1);
    while(q --){
        int k = read<int>(), lca, ans = INF;
        uF(i, 1, k){
            p[i] = read<int>();
            lca = i == 1 ? p[i] : LCA(lca, p[i]);
        }
        uF(i, 1, k) pc[i] = Query(p[i], lca);
        uf(s, 1, 1 << k){
            uF(i, 1, k) if(s >> i - 1 & 1){
                dp[s] = dp[s - (1 << i - 1)] | pc[i];
                break;
            }
            int cnt = 0;
            uf(i, 0, k) cnt += s >> i & 1;
            updmin(ans, (int)(dp[s].count() / cnt));
        }
        write(ans * k, "\n");
    }
    lovely _lzy_;
}