MapGuessing TopCoder

MapGuessing TopCoder - 12152

做得我很迷

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

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

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

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

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

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

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

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

估计一下这个( ext{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;
	}
};

原文地址:https://www.cnblogs.com/chasedeath/p/13111698.html