CF1326F2 Wise Men (Hard Version) 题解

题目链接

点击打开链接

题目解法

挺难的。可能一步一步推下来也没那么复杂(?
基本 copy tzc_wk 的题解/bx

肯定不能像 \(F1\) 用普通的状压求,一个技巧是容斥
考虑令 \(f_S\) 表示 \(S\) 中为 \(1\) 的位置 \(p_i\)\(p_{i+1}\) 必须认识,为 \(0\) 的位置随便
\(f\) 数组相当于答案的高维后缀和,若求出 \(f\),还原答案是简单的

考虑如何求出 \(f\)
我们把 \(S\) 的二进制表示拆分成若干个连续 \(1\) 段,假设长度为 \(l_1,l_2,...,l_m\)
这相当于把全集分成 \(m\) 个集合,满足 \(|S_i|=l_i\),然后把 \(S_i\) 内部钦定一个顺序,满足相邻两个都认识 的方案数
后面的部分是好做的,令 \(dp_{S,i}\) 表示当前有集合 \(S\) 中的人,最后一个人是 \(i\),且相邻人都认识的方案数,转移是 simple 的
前面的部分比较有启发性
\(g_S\) 表示 \(S\) 集合内任意排序,相邻人都认识的方案数
那么总方案数就是 \(\prod \limits_{|S_i|=l_i,S_1|S_2|...|S_m=全集} g_{S_i}\)
这东西看上去类似子集卷积
我们在记一维 \(popcount\),然后对每个 \(popcount\) 做一遍 \(fwt\)
然后再把 \(l_i\) 对应的点值序列都对应位相乘,最后 \(ifwt\) 还原回去即可
注意到我们只需要全集的值,所以这一部分并不需要显式的 \(ifwt\),直接容斥即可

但这样做复杂度是 \(O(4^nn)\),显然不太行
但我们发现 \(l_i\) 的顺序是不影响答案的,所以我们只求 \(l_i\) 不降的方案数即可
不难发现这相当于枚举 \(n\) 的拆分数,而 \(18\) 的拆分数个数只有 \(385\)

所以最后的时间复杂度为 \(O(385\times 2^n+2^nn^2)\)

#include <bits/stdc++.h>
#define F(i,x,y) for(int i=(x);i<=(y);i++)
#define DF(i,x,y) for(int i=(x);i>=(y);i--)
#define ms(x,y) memset(x,y,sizeof(x))
#define SZ(x) (int)x.size()-1
#define pb push_back
using namespace std;
typedef long long LL;
typedef unsigned long long ull;
typedef pair<int,int> pii;
template<typename T> void chkmax(T &x,T y){ x=max(x,y);}
template<typename T> void chkmin(T &x,T y){ x=min(x,y);}
template<typename T> void read(T &FF){
    FF=0;int RR=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;
    for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;
    FF*=RR;
}
typedef vector<int> vi;
const int N=18;
int n,ppc[1<<N];
LL ans[1<<N],rec[N+1][1<<N],f[1<<N][N];
bool E[N][N];
int all(int x){ return (1<<x)-1;}
void fwtor(LL *val){ F(i,0,n-1) F(S,0,(1<<n)-1) if(S>>i&1) val[S]+=val[S^(1<<i)];}
map<vi,LL> mp;
vi dvd;
LL mul[N+1][1<<N];
void doit(){
    LL res=0;
    F(S,0,all(n)){
        if((n-ppc[S])&1) res-=mul[dvd.size()][S];
        else res+=mul[dvd.size()][S];
    }
    mp[dvd]=res;
}
void dfs(int lef,int dep){
    if(!lef){ doit();return;}
    F(i,dep,lef){
        dvd.pb(i);
        F(S,0,(1<<n)-1) mul[dvd.size()][S]=mul[SZ(dvd)][S]*rec[i][S];
        dfs(lef-i,i);
        dvd.pop_back();
    }
}
int main(){
    ios::sync_with_stdio(false);cin.tie(nullptr),cout.tie(nullptr);
    cin>>n;
    F(i,0,n-1){
        string str;cin>>str;
        F(j,0,n-1) E[i][j]=str[j]-48;
    }
    F(S,0,all(n)) ppc[S]=__builtin_popcount(S);
    F(i,0,n-1) f[1<<i][i]=1;
    F(S,1,all(n)) F(i,0,n-1) if(f[S][i]){
        rec[ppc[S]][S]+=f[S][i];
        F(j,0,n-1) if(!(S>>j&1)&&E[i][j]) f[S^1<<j][j]+=f[S][i];
    }
    F(i,1,n) fwtor(rec[i]);
    F(i,0,all(n)) mul[0][i]=1;
    dfs(n,1);
    F(S,0,all(n-1)){
        dvd.clear();
        F(i,0,n-1){
            int j=i;
            while(j<n-1&&(S>>j&1)) j++;
            dvd.pb(j-i+1),i=j;
        }
        sort(dvd.begin(),dvd.end());
        ans[S]=mp[dvd];
    }
    F(i,0,n-2) F(S,0,all(n-1)) if(S>>i&1) ans[S^(1<<i)]-=ans[S];
    F(i,0,all(n-1)) printf("%lld ",ans[i]);puts("");
    return 0;
}

posted @ 2025-02-07 22:59  Farmer_D  阅读(28)  评论(0)    收藏  举报