JZOJ ???? party

没有传送门
题目大意

给你一棵 n 个结点的数,每个结点有一种特产,用 a i 表示。总共有 m 种特产。有 q 个询问,每个询问形如:有 c 个人,分别在某些城市,现在他们要走到 LCA 处。他们能够买沿途的特产,要求:

  1. 每种特产要买只能买一件;
  2. 每个人买的特产数量要相等;
  3. 所有人买的特产都不能重复。

求最多能买多少种(件)特产。

n 3 × 10 5 q 5 × 10 4 m 10 3 c 5

Limited Constraint: n 3000 q 3000

考场上的思路

考虑做 Limited Constraint。求 LCA 无话可说, O ( n log n ) 倍增即可。每个询问暴力往上走,记录出现了哪些特产,现在的问题是如何求答案。

考虑网络流。把每种特产看作一个点,向汇点连一条容量为 1 的边,把每个人看成一个点,向能够买的特产连一条容量为 1 的边。不能够直接求最大流,考虑二分答案。如果二分答案后求最大流满流,说明可行,否则需要减少答案。

使用 Dinic 进行求解,可以认为每次求解的时间复杂度为 O ( n + m ) (我口胡的,不过你可以认为多路增广速度不错)。极限数据(两条链,所有特产都出现)要跑大约 3 sec,但出题人良心,直接让我过了 QAQ。

也想过树链剖分,但是感觉没卵用。

思路

考虑树链剖分 QAQ,不管怎么说把每个人能买的特产求出来再说。由于 m 比较小,又必须具体地求出每个人能买的特产种类(不然接下来怎么做 QAQ),因此考虑 bitset。

直接用树链剖分 + 线段树 + bitset 的时间复杂度是 O ( m ω log 2 n ) 的,算起来快 10000 了,不太可过。考虑用预处理优化一下。预处理出每个结点到链顶的 bitset,查询时除了最后一段,其它的都直接用预处理后的值。预处理的时间复杂度是 O ( n m ω ) 的,单词查询的时间复杂度是 O ( m ω log n ) 的。

(这里提醒一下,虽然 n m 看上去蛮大的,但是 m ω 蛮小的。取 ω = 32 时,你可以认为它的大小仅为 32

不考虑接下来怎么做,目前我们能够在 O ( q m ω log n ) 的时间复杂度内求出每个人能买的特产,很优秀了。

接下来不要用网络流了,要用一个叫做 Hall 定理的东西。

Hall 定理

完全没有听说过啊 QAQ。

Hall 定理是跟二分图完美匹配有关的东西,先来一个定义:

完美匹配:若一个二分图的最大匹配数为 min ( | X | , | Y | ) ,则称该二分图存在完美匹配。

Hall 定理:

二分图存在完美匹配当且仅当 X 中的任意 k 个点至少与 Y k 点有连边。

唉,我也不想学习怎么证明了,这里是王师傅的证明。一年前王师傅在我还在玩泥巴的时候就会了,太强辣!

继续怎么做呢?

直接把之前的网络流图搬过来肯定不行啊,那个不是匹配。不过考虑一个很笨的方法:如果之前我们不是通过修改源点到人的容量,而是加点呢?虽然在网络流中这个做法很傻,但是在不用网络流的这里就很聪明了——这个做法保证了求的东西是二分图的最大匹配。显然的是,如果一个答案合法,那么这个二分图就有完美匹配,也就是说任意 k 个点都至少能买 k 个特产(有买 k 个特产的机会)。

对于同一个人来说,根本不用检查都属于他的点:只要这个人能买 k 个特产,把他拆成 k 个点他还是能买 k 个特产。所以问题就在于不同的人身上。我们把不同的人组合起来,分别检查是否满足霍尔定理即可。检查的时间复杂度为 O ( 2 c m ω )

由于要满足霍尔定理,因此 a n s × | S | c h e c k ,所以答案为检查值 c h e c k 除以人数的下取整的最小值(因为要任意)。

参考代码
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <cassert>
#include <cctype>
#include <climits>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
#include <stack>
#include <queue>
#include <deque>
#include <map>
#include <set>
#include <bitset>
#include <list>
#include <functional>
typedef long long LL;
typedef unsigned long long ULL;
using std::cin;
using std::cout;
using std::endl;
typedef int INT_PUT;
INT_PUT readIn()
{
    INT_PUT a = 0; bool positive = true;
    char ch = getchar();
    while (!(ch == '-' || std::isdigit(ch))) ch = getchar();
    if (ch == '-') { positive = false; ch = getchar(); }
    while (std::isdigit(ch)) { a = a * 10 - (ch - '0'); ch = getchar(); }
    return positive ? -a : a;
}
void printOut(INT_PUT x)
{
    char buffer[20]; int length = 0;
    if (x < 0) putchar('-'); else x = -x;
    do buffer[length++] = -(x % 10) + '0'; while (x /= 10);
    do putchar(buffer[--length]); while (length);
    putchar('\n');
}

const int INF = (~(int(1) << (sizeof(int) * 8 - 1))) >> 1;
const int maxn = int(3e5) + 5;
const int maxm = int(1e3) + 5;
int n, m, q;
int a[maxn];
int LCA(int, int);
struct Query
{
    int c[6];
    int lca;
    void read()
    {
        c[0] = readIn();
        for (int i = 1; i <= c[0]; i++)
            c[i] = readIn();
        lca = LCA(c[1], c[2]);
        for (int i = 3; i <= c[0]; i++)
            lca = LCA(lca, c[i]);
    }
} querys[maxn];

typedef std::vector<std::vector<int> > Graph;
Graph G;
int parent[maxn];
int depth[maxn];
int size[maxn];
int heavy[maxn];
void DFS1(int node)
{
    depth[node] = depth[parent[node]] + 1;
    size[node] = 1;
    for (int i = 0; i < G[node].size(); i++)
    {
        int to = G[node][i];
        DFS1(to);
        size[node] += size[to];
        if (!heavy[node] || size[to] > size[heavy[node]])
            heavy[node] = to;
    }
}
int stamp;
int dfn[maxn];
int seq[maxn];
int top[maxn];
void DFS2(int node, int t)
{
    stamp++;
    dfn[node] = stamp;
    seq[stamp] = node;
    if (!t)
        t = node;
    top[node] = t;
    if (heavy[node])
        DFS2(heavy[node], t);
    for (int i = 0; i < G[node].size(); i++)
    {
        int to = G[node][i];
        if (to == heavy[node]) continue;
        DFS2(to, 0);
    }
}

int LCA(int u, int v)
{
    while (top[u] != top[v])
    {
        if (depth[top[u]] < depth[top[v]])
            std::swap(u, v);
        u = parent[top[u]];
    }
    return depth[u] < depth[v] ? u : v;
}

#define RunInstance(x) delete new x
struct brute
{
    int stamp;
    int vis[maxm];
    int appear[6][maxm];

    struct NetworkFlow
    {
        struct Edge
        {
            int from, to, cap, flow;
            Edge() {}
            Edge(int from, int to, int cap) : from(from), to(to), cap(cap), flow() {}
        };
        Graph G;
        std::vector<Edge> edges;
        int addEdge(int from, int to, int cap)
        {
            edges.push_back(Edge(from, to, cap));
            edges.push_back(Edge(to, from, 0));
            int i = edges.size();
            G[from].push_back(i - 2);
            G[to].push_back(i - 1);
            return i - 2;
        }
        void operator=(const NetworkFlow& b)
        {
            G = b.G;
            edges = b.edges;
            s = b.s;
            t = b.t;
        }

        int s, t;

        int level[maxn];
        std::vector<int> cur;
        int Dinic(int node, int opt)
        {
            if (node == t || !opt) return opt;
            int flow = 0;
            for (int& i = cur[node]; i < G[node].size(); i++)
            {
                int t;
                Edge& e = edges[G[node][i]];
                if (e.flow < e.cap && level[node] + 1 == level[e.to] &&
                    (t = Dinic(e.to, std::min(opt, e.cap - e.flow))))
                {
                    flow += t;
                    opt -= t;
                    e.flow += t;
                    edges[G[node][i] ^ 1].flow -= t;
                    if (!opt) break;
                }
            }
            return flow;
        }

        struct Queue
        {
            int c[maxn];
            int head, tail;
            Queue() : head(), tail() {}
            void clear() { head = tail = 0; }
            void push(int x) { c[tail++] = x; }
            void pop() { head++; }
            int front() { return c[head]; }
            bool empty() { return head == tail; }
        } q;
        int stamp;
        int vis[maxn];
        NetworkFlow() : stamp(), vis() {}
        bool BFS()
        {
            q.clear();
            stamp++;
            vis[s] = stamp;
            q.push(s);
            level[s] = 0;
            while (!q.empty())
            {
                int from = q.front();
                q.pop();
                for (int i = 0; i < G[from].size(); i++)
                {
                    const Edge
                        & e = edges[G[from][i]];
                    if (e.flow < e.cap && vis[e.to] != stamp)
                    {
                        vis[e.to] = stamp;
                        level[e.to] = level[from] + 1;
                        q.push(e.to);
                    }
                }
            }
            return vis[t] == stamp;
        }
        int maxflow()
        {
            int flow = 0;
            while (BFS())
            {
                cur.clear();
                cur.resize(G.size());
                flow += Dinic(s, INF);
            }
            return flow;
        }
    } nf, backup;

    brute() : stamp(), vis(), appear()
    {
        backup.G.resize(m + 7);
        backup.s = 0;
        backup.t = m + 1;
        for (int i = 1; i <= m; i++)
            backup.addEdge(i, backup.t, 1);

        for (int i = 1; i <= q; i++)
        {
            const int* c = querys[i].c;
            int lca = querys[i].lca;
            for (int j = 1; j <= c[0]; j++)
            {
                stamp++;
                appear[j][0] = 0;
                int cnt = c[j];
                while (true)
                {
                    if (vis[a[cnt]] != stamp)
                    {
                        vis[a[cnt]] = stamp;
                        appear[j][++appear[j][0]] = a[cnt];
                    }
                    if (cnt == lca)
                        break;
                    cnt = parent[cnt];
                }
            }

            int l = 0, r = m / c[0] + 1;
            while (r - l > 1)
            {
                int mid = (l + r) >> 1;
                nf = backup;
                for (int j = 1; j <= c[0]; j++)
                    for (int k = appear[j][0]; k; k--)
                        nf.addEdge(m + 1 + j, appear[j][k], 1);
                for (int j = 1; j <= c[0]; j++)
                    nf.addEdge(nf.s, m + 1 + j, mid);
                if (nf.maxflow() == mid * c[0])
                    l = mid;
                else
                    r = mid;
            }
            printOut(l * c[0]);
        }
    }
};
struct work
{
    typedef std::bitset<1024> bitset;
    class SegTree
    {
        static inline int code(int l, int r)
        {
            return (l + r) | (l != r);
        }
        bitset nodes[maxn * 2];

        int g_L, g_R;
        bitset query_(int l, int r)
        {
            if (g_L <= l && r <= g_R)
            {
                return nodes[code(l, r)];
            }
            int mid = (l + r) >> 1;
            bitset ret;
            if (g_L <= mid) ret |= query_(l, mid);
            if (g_R > mid) ret |= query_(mid + 1, r);
            return ret;
        }

    public:
        SegTree() { build(1, n); }
        void build(int l, int r)
        {
            if (l == r)
            {
                nodes[code(l, r)].set(a[seq[l]]);
                return;
            }
            int mid = (l + r) >> 1;
            build(l, mid);
            build(mid + 1, r);
            nodes[code(l, r)] = nodes[code(l, mid)] | nodes[code(mid + 1, r)];
        }
        bitset query(int l, int r)
        {
            g_L = l;
            g_R = r;
            return query_(1, n);
        }
    } st;
    bitset topset[maxn];
    void DFS3(int node)
    {
        if (node == top[node])
            topset[node].set(a[node]);
        else
            (topset[node] = topset[parent[node]]).set(a[node]);
        for (int i = 0; i < G[node].size(); i++)
        {
            int to = G[node][i];
            DFS3(to);
        }
    }

    bitset query(int u, int lca)
    {
        bitset ret;
        while (top[u] != top[lca])
        {
            ret |= topset[u];
            u = parent[top[u]];
        }
        ret |= st.query(dfn[lca], dfn[u]);
        return ret;
    }

    work()
    {
        DFS3(1);
        for (int i = 1; i <= q; i++)
        {
            bitset set[6];
            const int* c = querys[i].c;
            int lca = querys[i].lca;
            for (int j = 1; j <= c[0]; j++)
                set[j] = query(c[j], lca);

            int ans = m;
            int U = 1 << c[0];
            for (int S = 1; S < U; S++)
            {
                int cnt = 0;
                set[0].reset();
                for (int j = 0; j < c[0]; j++)
                    if (S & (1 << j))
                    {
                        set[0] |= set[j + 1];
                        cnt++;
                    }
                ans = std::min(ans, (int)set[0].count() / cnt);
            }
            printOut(ans * c[0]);
        }
    }
};

void run()
{
    n = readIn();
    m = readIn();
    q = readIn();
    if (!q)
        return;
    G.resize(n + 1);
    for (int i = 2; i <= n; i++)
        G[parent[i] = readIn()].push_back(i);
    for (int i = 1; i <= n; i++)
        a[i] = readIn();
    DFS1(1);
    DFS2(1, 0);
    for (int i = 1; i <= q; i++)
        querys[i].read();

    if (n <= 3000 && q <= 3000)
        RunInstance(brute);
    else
        RunInstance(work);
}

int main()
{
#ifndef LOCAL
    freopen("party.in", "r", stdin);
    freopen("party.out", "w", stdout);
#endif
    run();
    return 0;
}

猜你喜欢

转载自blog.csdn.net/lycheng1215/article/details/80954768
今日推荐