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} })$

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#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;
}
};