MapGuessing TopCoder - 12152 (dfs容斥)

MapGuessing TopCoder - 12152

做得我很迷

首先是可以把问题转化为,每次操作之后会让原序列的限制条件变为:不考虑某一些位置时合法

枚举每个开始位置,依次考虑每一个操作,如果有一个位置被改为不同,就是不合法的

对于每一个开始位置,能得到的的最优限制条件都是唯一的,因为只要是合法的,一定取最后一个合法的位置,才能尽可能多地覆盖一些位置

那么我们得到了\(|goal|\)个这样的限制条件\(S_i\),设\(n=|goal|\)

直接计算肯定会算重,考虑一个简单的容斥

\(\begin{aligned}Answer=\sum_{T\ne \empty}(-1)^{|T|+1}2^{|S_{T_1}\cap \cdots \cap S_{T_{|T|}}|}\end{aligned}\)

就是枚举选择一个限制的集合,求出他们的并集

直接枚举复杂度当然是\(O(2^{|n|})\),如果\(\text{dfs}\)枚举,当前状态为\(0\)时,可以直接返回答案

估计一下这个\(\text{dfs}\)的复杂度

设操作过程中指针左右移动的距离是\(L\),那么最多存在\(n-L\)个合法的开始位置,每个状态最多包含\(L\)\(1\)

很显然枚举的上限是\(\min\{2^{n-L},2^{L}\}\),即受到开始位置的个数可能出现的1个数的限制

\(n-L=L\)时,复杂度达到上限是\(O(2^{\frac{n}{2}})\)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define rep(i,a,b) for(int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(int i=a,i##end=b;i>=i##end;--i)

set <ll> st;
ll S[40];
int now[40],cnt;
ll dfs(int p,ll State){
	if(p==cnt+1) return (1ll<<__builtin_popcountll(State)); // 枚举完毕
	if(State==0) return 0; // 剪枝,注意因为是后面的所有1,-1相加,所以是return 0而不是1
	return dfs(p+1,State)-dfs(p+1,State&S[p]);  // 直接处理容斥系数
}

class MapGuessing {
public:
	long long countPatterns(string S, vector <string> code) {
		string C="";
		for(string t:code) C+=t;
		int n=S.size();
		st.clear();
		rep(i,0,n-1) {
			int p=i,f=1;
			ll lst=0; // 记录最后一个合法的即可
			rep(i,0,n-1) now[i]=0;
			for(char c:C) {
				if(c=='<') p--;
				if(c=='>') p++;
				if(c=='0') now[p]=1;
				if(c=='1') now[p]=2;
				if(p<0 || p>=n){ f=0; break; }
				int fl=1;
				ll T=0;
				rep(i,0,n-1) {
					if(!now[i]) continue;
					if(now[i]-1!=S[i]-'0'){ fl=0; break; }
					else T|=1ll<<i;
				}
				if(!fl) continue;
				lst=T;
			}
			if(!f) continue;
			st.insert(-lst);
		}
		cnt=0;
		for(ll x:st) {
			ll y=-x;
			int f=1;
			rep(i,1,cnt) if((::S[i]&y)==y){ f=0; break; }
			if(f) ::S[++cnt]=y;
		}
        //初始系数是-1
		ll ans=(1ll<<n)-dfs(1,(1ll<<n)-1); // 注意枚举出来会把T=emptyset的情况算进去,要去掉
		return ans;
	}
};

posted @ 2020-06-13 09:30  chasedeath  阅读(164)  评论(0编辑  收藏  举报