JZOJ5824.party

題意:

數據範圍:

Analysis

首先考慮辦聚會的城市,由於道路是單向的,不難發現該城市就是這c 個城市的lca .
考慮選擇禮品的限制如何解決,思考一下發現是對於每一個禮品分配城市,考慮網絡流解決.
至於所有城市的禮品數一樣我們可以二分限制最大流,判斷是否能滿流即可。
我考場上考慮到這裏,事實上已經能拿到65 分了,本來想能不能最大流轉最小割做dp ,發現並不行。
運用Hall 定理,每一次二分,可以相當於把每個城市拆成mid 個點,兩邊二分圖匹配,只要知道是否有完備匹配即可,那麼根據Hall 定理,兩邊有完備匹配的必要條件是:
左部任意一個子集相鄰的點都>= 子集大小。
那麼每一個子集都找出它最多能夠給每個城市分配多少禮品,即右方相鄰點數目除以子集大小。
至於每一個點到聚會城市的顏色數目,顏色種類只有1000 ,直接線段樹維護一下bitset ,用樹鏈剖分解決,預處理每一個點到鏈頂的bitset ,複雜度可以做到O(qclogn1000w)

Code

# include<cstdio>
# include<cstring>
# include<algorithm>
# include<bitset>
using namespace std;
const int N = 3e5 + 5;
const int M = 1e3 + 5;
bitset <M> c[N << 2],s[N],a[15],las;
int in[N],top[N],fa[N],dep[N],siz[N],son[N],id[N];
int v[N],to[N],nx[N],st[N],z[15];
int n,m,tot,q;
inline int read()
{
    int x = 0; char ch = getchar();
    for (; ch < '0' || ch > '9' ; ch = getchar());
    for (; ch >= '0' && ch <= '9' ; ch = getchar()) x = x * 10 + ch - '0';
    return x;
}
void add(int u,int v) { to[++tot] = v,nx[tot] = st[u],st[u] = tot; }
inline void build(int x,int l,int r)
{
    if (l == r) { c[x][v[id[l]]] = 1; return; }
    int mid = (l + r) >> 1;
    build(x << 1,l,mid); build(x << 1 | 1,mid + 1,r);
    c[x] = c[x << 1] | c[x << 1 | 1];
}
inline void qry(int x,int l,int r,int l1,int r1,int p)
{
    if (l >= l1 && r <= r1) { a[p] |= c[x]; return; }
    int mid = (l + r) >> 1;
    if (l1 <= mid) qry(x << 1,l,mid,l1,r1,p);
    if (r1 > mid) qry(x << 1 | 1,mid + 1,r,l1,r1,p);
}
void dfs(int x)
{
    siz[x] = 1;
    for (int i = st[x] ; i ; i = nx[i])
    {
        dfs(to[i]),siz[x] += siz[to[i]];
        if (siz[to[i]] > siz[son[x]]) son[x] = to[i];
    }
}
void dfs1(int x,int f,int t)
{
    in[x] = ++tot,id[tot] = x,top[x] = t;
    if (x != t) s[x] = s[f]; s[x][v[x]] = 1;
    if (son[x]) dfs1(son[x],x,t);
    for (int i = st[x] ; i ; i = nx[i])
    if (to[i] != son[x]) dfs1(to[i],x,to[i]);
}
inline int find(int x,int y)
{
    while (top[x] != top[y])
    {
        if (dep[top[x]] < dep[top[y]]) swap(x,y);
        x = fa[top[x]];
    }
    return dep[x] < dep[y] ? x : y;
}
inline void solve(int x,int y,int p)
{
    while (top[x] != top[y])
    {
        a[p] |= s[x];
        x = fa[top[x]];
    }
    qry(1,1,n,in[y],in[x],p);
}
int main()
{
    n = read(),m = read(),q = read();
    for (int i = 2 ; i <= n ; ++i) fa[i] = read(),dep[i] = dep[fa[i]] + 1,add(fa[i],i);
    for (int i = 1 ; i <= n ; ++i) v[i] = read();
    tot = 0,dfs(1);
    dfs1(1,0,1); build(1,1,n);
    while (q--)
    {
        int num = read();
        for (int i = 1  ; i <= num ; ++i) z[i] = read(),a[i].reset();
        int lca = z[1];
        for (int i = 2 ; i <= num ; ++i) lca = find(lca,z[i]);
        for (int i = 1 ; i <= num ; ++i) solve(z[i],lca,i);
        int ans = M;
        for (int i = 1 ; i < (1 << num) ; ++i)
        {
            int cnt = 0; las.reset();
            for (int j = 1 ; j <= num ; ++j)
            if (i & (1 << (j - 1))) ++cnt,las |= a[j];
            ans = min(ans,(int)las.count() / cnt);
        }
        printf("%d\n",ans * num);
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章