peiwenjun's blog 没有知识的荒原

HDU6355 Fireflies 题解

题目描述

\(T\) 组数据,定义全集由 \(\prod_{i=1}^np_i\)\(n\) 维向量组成,其中 \(1\le x_i\le p_i\)

对于两个向量 \(X=(x_1,\cdots,x_n),Y=(y_1,\cdots,y_n)\) ,如果 \(x_i-y_i\ge 0,\sum_{i=1}^n(x_i-y_i)=1\) ,那么连一条有向边 \(X\to Y\)

求这个集合的可重最小链覆盖。

数据范围

  • \(1\le T\le 2000,1\le n\le 32,1\le p_i\le 10^9\)

时间限制 \(\texttt{2.5s}\) ,空间限制 \(\texttt{128MB}\)

分析

看到可重先做一次传递闭包,那么 \(X\to Y\) 连边当且仅当 \(\forall 1\le i\le n,x_i\ge y_i\)

根据\(\text{dilworth}\)定理,最小不可重链覆盖等于最长反链,题意转化为选尽可能多的向量,使得两两没有偏序关系。

根据Sperner定理的推广,我们应该选择满足 \(\sum x_i=c\) 的所有向量,其中 \(c=\lfloor\frac{\sum(p_i+1)}2\rfloor\) 为定值。

于是这道题变成了一个经典问题:求满足 \(1\le x_i\le p_i,\sum_{i=1}^nx_i=c\) 的方案数。

直接容斥,可以算出答案:

\[\sum_{S\subseteq\{1,2,\cdots,n\}}(-1)^{|S|}\binom{c-1-\sum\limits_{i\in S}p_i}{n-1} \]

至此我们获得了一个 \(\mathcal O(Tn2^n)\) 的做法。


考虑折半优化。

\(L=\{1,\cdots,\lfloor\frac n2\rfloor\},R=\{\lfloor\frac n2\rfloor+1,\cdots,n\},f(S)=\sum\limits_{i\in S}p_i\)

\[\sum_{S\subseteq\{1,2,\cdots,n\}}(-1)^{|S|}\binom{c-1-f(S)}{n-1}\\ =\sum_{S\subseteq L}\sum_{T\subseteq R}(-1)^{|S|+|T|}\binom{c-1-f(S)-f(T)}{n-1}\\ =\sum_{S\subseteq L}\sum_{T\subseteq R}(-1)^{S+T}\sum_{i=0}^{n-1}\binom{c-1-f(S)}i\binom{-f(T)}{n-1-i}\\ =\sum_{i=0}^{n-1}\sum_{S\subseteq L}(-1)^{|S|}\binom{c-1-f(S)}i\cdot\sum_{T\subseteq R}(-1)^{|T|}\binom{-f(T)}{n-1-i} \]

这里用到组合恒等式 \(\binom{n+m}k=\sum_{i=0}^k\binom ni\binom m{k-i}\)

注意 \(n+m\lt 0\)不应计入贡献,因此我们需要对 \(f(S),f(T)\) 排序后双指针

时间复杂度 \(\mathcal O(Tn2^\frac n2)\)

#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define fi first
#define se second
#define mp make_pair
#define pii pair<ll,int>
using namespace std;
const int maxn=40,mod=1e9+7;
int m,n,t;
ll c;
int p[maxn];
vector<pii> a,b;
struct mint
{
    int val;
    mint (ll _val=0)
    {
        val=_val%mod;
        if(val<0) val+=mod;
    }
}inv[maxn],sum[maxn];
inline mint operator+(mint x,mint y)
{
    if((x.val+=y.val)>=mod) x.val-=mod;
    return x;
}
inline mint operator-(mint x,mint y)
{
    if((x.val-=y.val)<0) x.val+=mod;
    return x;
}
inline mint operator*(mint x,mint y)
{
    static ull p=mod,m=(1ull<<63)/p;
    ull c=(ull)x.val*y.val;
    c-=((__int128)c*m>>63)*p;
    if(c>=p) c-=p;
    return c;
}
inline void operator+=(mint &x,mint y)
{
    x=x+y;
}
inline void operator-=(mint &x,mint y)
{
    x=x-y;
}
inline void operator*=(mint &x,mint y)
{
    x=x*y;
}
inline mint qpow(mint a,int k)
{
    mint res=1;
    for(;k;a=a*a,k>>=1) if(k&1) res=res*a;
    return res;
}
void work(vector<pii> &a,int *p,int n)
{
    a.resize(1<<n);
    for(int s=0;s<1<<n;s++)
    {
        ll cur=0;
        for(int i=1;i<=n;i++) if(s>>(i-1)&1) cur+=p[i];
        a[s]=mp(cur,__builtin_parity(s)?-1:1);
    }
}
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n),c=0,m=n>>1;
        for(int i=1;i<=n;i++) scanf("%d",&p[i]),c+=p[i]+1;
        inv[0]=1;
        for(int i=1;i<=n-1;i++) inv[i]=inv[i-1]*qpow(i,mod-2);
        c>>=1,work(a,p,m),work(b,p+m,n-m);
        sort(a.begin(),a.end(),greater<pii>());
        sort(b.begin(),b.end());
        memset(sum,0,sizeof(sum));
        mint res=0;
        for(int i=0,j=0;i<a.size();i++)
        {
            while(j<b.size()&&a[i].fi+b[j].fi<=c-1)
            {
                mint cur=b[j].se;
                for(int k=0;k<=n-1;k++) sum[n-1-k]+=cur*inv[k],cur*=-b[j].fi-k;
                j++;
            }
            mint cur=a[i].se;
            for(int k=0;k<=n-1;k++) res+=cur*inv[k]*sum[k],cur*=c-1-a[i].fi-k;
        }
        printf("%d\n",res.val);
    }
    return 0;
}

posted on 2023-03-21 17:46  peiwenjun  阅读(10)  评论(0)    收藏  举报

导航