• CF1326F2 Wise Men (Hard Version)


    Wise Men (Hard Version)

    (n) wise men live in a beautiful city. Some of them know each other.

    For each of the n! possible permutations (p_1, p_2, ldots, p_n) of the wise men, let's generate a binary string of length (n-1): for each (1 leq i < n) set (s_i=1) if (p_i) and (p_{i+1}) know each other, and (s_i=0) otherwise.

    For all possible (2^{n-1}) binary strings, find the number of permutations that produce this binary string.

    (2 leq n leq 18)

    题解

    For each binary string s, let's calculate (f(s)) – the number of permutations, such that, if (s_i=1), then (p_i) and (p_{i+1}) know each other, otherwise, they may know or don't know each other.

    To get real answers, we may use inclusion-exclusion, which may be optimized using a straightforward sum over subsets dp. 说的就是高维前缀差。

    To calculate (f(s)), we need to note that (f) depends only on the multiset of lengths of blocks of (1)'s in it.

    For example, (f(110111) = f(111011)), because the multiset of lengths is ({1, 3, 4}) (note that block of size (x) of (1)'s corresponds to length (x+1)). 一个 (0) 也对应了一个长度为 (1) 的块。

    And note that there are exactly (P(n)) (the number of partitions of (n)) possible multisets.

    (P(18) = 385)

    To process further, at first let's calculate (g(len, mask)) – the number of paths of length (len), which pass only through the vertices from (mask) (and only through them).

    You can calculate it with a straightforward (dp(mask, v)) in (mathcal{O}{(2^n cdot n^2)}).

    Then, let's fix the multiset of lengths (a_1, a_2, ldots, a_k).

    I claim that the (f(s)) for this multiset is equal to (sum{prod{g(a_i, m_i)}}) over all masks (m_1, m_2, ldots m_k), such that the bitwise OR of (m_1, m_2, ldots, m_k) is equal to (2^n-1) (note that we don't care about the number of bits like in a usual non-intersecting subsets convolution, because if some masks are intersecting, then their OR won't be equal to (2^n-1) because (sum{a_i} = n)).

    You can calculate this sum by changing (g(len)) to the sum over subsets. 对 (g(len)) 做高维前缀和。

    And then, for this partition, you can just calculate (d(mask)) = (prod{g(a_i, mask)}) in (mathcal{O}{(k cdot 2^n)}), and you can restore the real value of (2^n-1) by inclusion-exclusion in (mathcal{O}{(2^n)}). 注意到 (d(mask)) 统计的方案只保证了经过的点是 (mask) 的子集,所以还要进行一个强制枚举少走了那些点的容斥。

    If you will calculate this naively, you will get the (mathcal{O}(( ext{sum of sizes of all partitions}) cdot 2^n)) solution, which is enough to get AC. 如果你枚举出了划分后再暴力算 (d(mask)) 的话就是这个复杂度。

    But you can optimize this because you can maintain (d(mask)) during the brute force of all partitions. And in the tree of all partitions, there are (mathcal{O}{(P(n))}) intermediate vertices, so it will work in (mathcal{O}{(P(n) cdot 2^n)}). 在DFS的过程中顺便维护一下 (d(mask)) 就是这个复杂度。

    The total complexity is (mathcal{O}{((P(n) + n^2) cdot 2^n))}).

    CO int N=18;
    int e[N];
    int64 dp[1<<N][N],g[N+1][1<<N];
    int64 d[1<<N],cur[N+1][1<<N];
    int64 f[1<<N];
    
    int main(){
    	int n=read<int>();
    	for(int i=0;i<n;++i){
    		static char s[N];scanf("%s",s);
    		for(int j=0;j<n;++j)if(s[j]=='1') e[i]|=1<<j;
    	}
    	for(int i=0;i<n;++i) dp[1<<i][i]=1;
    	for(int mask=0;mask<1<<n;++mask)
    		for(int i=0;i<n;++i)if(dp[mask][i]){
    			for(int j=0;j<n;++j)if(~mask>>j&1 and e[i]>>j&1)
    				dp[mask|1<<j][j]+=dp[mask][i];
    			g[popcount(mask)][mask]+=dp[mask][i];
    		}
    	for(int len=1;len<=n;++len){
    		for(int i=0;i<n;++i)
    			for(int mask=0;mask<1<<n;++mask)if(mask>>i&1)
    				g[len][mask]+=g[len][mask^1<<i];
    	}
    	map<vector<int>,vector<int> > ok;
    	for(int mask=0;mask<1<<(n-1);++mask){
    		int x=0;
    		vector<int> t;
    		for(;x<n;++x){
    			int len=1;
    			while(mask>>x&1) ++x,++len;
    			t.push_back(len);
    		}
    		sort(t.begin(),t.end());
    		ok[t].push_back(mask);
    	}
    	vector<int> a;
    	for(int mask=0;mask<1<<n;++mask) d[mask]=1;
    	function<void(int,int)> dfs=[&](int s,int last){ // sum len, last len
    		if(s==n){
    			int64 res=0;
    			int x=(1<<n)-1;
    			for(int mask=0;mask<1<<n;++mask){
    				if(popcount(mask)%2==0) res+=d[x^mask];
    				else res-=d[x^mask];
    			}
    			for(int c:ok[a]) f[c]+=res;
    			return;
    		}
    		if(s+last>n) return;
    		for(int mask=0;mask<1<<n;++mask) cur[s][mask]=d[mask];
    		for(int i=last;s+i<=n;++i){ // current len >= last len
    			if(s+i!=n and s+2*i>n) continue;
    			a.push_back(i);
    			for(int mask=0;mask<1<<n;++mask) d[mask]*=g[i][mask];
    			dfs(s+i,i);
    			for(int mask=0;mask<(1<<n);++mask) d[mask]=cur[s][mask];
    			a.pop_back();
    		}
    	};
    	dfs(0,1);
    	for(int i=0;i<n-1;++i)
    		for(int mask=0;mask<1<<(n-1);++mask)if(~mask>>i&1)
    			f[mask]-=f[mask|1<<i];
    	for(int mask=0;mask<1<<(n-1);++mask)
    		printf("%lld%c",f[mask]," 
    "[mask==(1<<(n-1))-1]);
    	return 0;
    }
    
  • 相关阅读:
    词汇表处理脚本
    jLowNote又,我为什么要说又,有bug
    于是按照贴吧某同学的指教,把imageViewer里那个愚蠢的语句改了
    捉到Bug一只,jLowNote里的
    高产赛母猪
    我超喜欢Nimbus风格的!
    专注写记事本三十年
    秒秒钟食言
    别再打了,Java和Python,你们其实都是C
    电话本写完了,发个1.0吧
  • 原文地址:https://www.cnblogs.com/autoint/p/12544403.html
Copyright © 2020-2023  润新知