标签:最优 set 序列 lin ace 简单 empty 答案 efi
做得我很迷
首先是可以把问题转化为,每次操作之后会让原序列的限制条件变为:不考虑某一些位置时合法
枚举每个开始位置,依次考虑每一个操作,如果有一个位置被改为不同,就是不合法的
对于每一个开始位置,能得到的的最优限制条件都是唯一的,因为只要是合法的,一定取最后一个合法的位置,才能尽可能多地覆盖一些位置
那么我们得到了\(|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;
}
};
MapGuessing TopCoder - 12152 (dfs容斥)
标签:最优 set 序列 lin ace 简单 empty 答案 efi
原文地址:https://www.cnblogs.com/chasedeath/p/13111698.html