版权声明:写得不好,转载请通知一声,还请注明出处,感激不尽 https://blog.csdn.net/As_A_Kid/article/details/87740263
Problem
Solution
看到数据范围,考虑折半搜索。按照拓扑序,把关键点集平分为左右两边来考虑。
如果我们dp出0到点x的路径条数 ,点x到1的路径条数 ,那么被这个点计数的路径条数显然是 。对于一条路径,我们用第一次经过的右半边的关键点(可能没有障碍)来计数,因此顺便把1号点也设为关键点。这样 只和左边障碍点状态有关, 只和右边障碍点状态有关。
我们可以暴力枚举左半边的障碍放置情况,然后dp出当前情况下右半边关键点的 。同样地,对右半边处理出 。注意到我们只关心路径条数的奇偶性,所以可以把所有关键点的 模2,再把这些放在一起即可得到01串。
注意到把各个位同时进行模2意义下的乘法,其真值表和按位与相同。那么我们把这些01串的状态用桶存起来,然后拼接就可以用FWT加速。这样在最终局面下,有偶数个1就说明共有偶数条路径,计数即可。
设障碍物个数为 ,时间复杂度
因为一个细节调了半天感觉药丸。。
Code
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <queue>
using namespace std;
typedef long long ll;
const int maxn=60,maxm=150000;
template <typename Tp> inline int getmin(Tp &x,Tp y){return y<x?x=y,1:0;}
template <typename Tp> inline int getmax(Tp &x,Tp y){return y>x?x=y,1:0;}
template <typename Tp> inline void read(Tp &x)
{
x=0;int f=0;char ch=getchar();
while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
if(ch=='-') f=1,ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
if(f) x=-x;
}
struct data{int v,nxt;}edge[510];
int n,p,tot,N,mi[32],t[maxn],head[maxn],in[maxn],top[maxn],pos[maxn];
int ban[maxn],f[maxn],mark[maxn],cnt[maxm];
ll ans,tl[maxm],tr[maxm];
char s[maxn];
queue<int> q;
int cmp(const int &a,const int &b){return pos[a]<pos[b];}
void insert(int u,int v){edge[++p]=(data){v,head[u]};head[u]=p;}
void fwt_and(ll *a,int f)
{
for(int i=1;i<N;i<<=1)
for(int j=0;j<N;j+=(i<<1))
for(int k=0;k<i;++k)
a[j+k]=(f==1?a[j+k]+a[j+k+i]:a[j+k]-a[j+k+i]);
}
void input()
{
int x,now=0;mi[0]=1;
read(n);
scanf("%s",s+1);
for(int i=1;i<=30;i++) mi[i]=mi[i-1]<<1;
for(int i=1;i<=n;i++) if(s[i]=='?') t[++tot]=i;
N=1<<(tot-(tot>>1)+1);
for(int i=1;i<=n;i++)
{
scanf("%s",s+1);
for(int j=1;j<=n;j++) if(s[j]=='Y'){insert(i,j);++in[j];}
}
for(int i=1;i<=n;i++) if(!in[i]) q.push(i);
while(!q.empty())
{
x=q.front();q.pop();pos[x]=++now;top[now]=x;
for(int i=head[x];i;i=edge[i].nxt)
{
--in[edge[i].v];
if(!in[edge[i].v]) q.push(edge[i].v);
}
}
sort(t+1,t+tot+1,cmp);
for(int i=(tot>>1)+1;i<=tot;i++) mark[t[i]]=1;
}
void workl()
{
int m=tot>>1,x,tmp;
for(int s=0;s<mi[m];s++)
{
memset(f,0,sizeof(f));
memset(ban,0,sizeof(ban));
tmp=0;f[1]=1;
for(int i=1;i<=m;i++) if(s&(1<<i-1)) ban[t[i]]=1;
for(int i=1;i<=n;i++)
if(!ban[top[i]]&&!mark[top[i]]&&f[top[i]])
{
x=top[i];
for(int j=head[x];j;j=edge[j].nxt) f[edge[j].v]^=f[x];
}
for(int i=(tot>>1)+1;i<=tot;i++) tmp|=f[t[i]]<<(i-m);
if(f[2]) tmp|=1;
++tl[tmp];
}
}
void workr()
{
int m=tot-(tot>>1),x,tmp;
for(int s=0;s<mi[m];s++)
{
memset(f,0,sizeof(f));
memset(ban,0,sizeof(ban));
tmp=0;f[2]=1;
for(int i=1;i<=m;i++) if(s&(1<<i-1)) ban[t[(tot>>1)+i]]=1;
for(int i=n;i>=1;i--)
if(!ban[top[i]])
{
x=top[i];
for(int j=head[x];j;j=edge[j].nxt) f[x]^=f[edge[j].v];
}
for(int i=(tot>>1)+1;i<=tot;i++) tmp|=f[t[i]]<<(i-(tot>>1));
if(f[2]) tmp|=1;
++tr[tmp];
}
}
int main()
{
input();
workl();
workr();
fwt_and(tl,1);fwt_and(tr,1);
for(int i=0;i<N;i++) tl[i]*=tr[i];
fwt_and(tl,-1);
for(int i=0;i<N;i++)
{
cnt[i]=cnt[i>>1]+(i&1);
if(~cnt[i]&1) ans+=tl[i];
}
printf("%lld\n",ans);
return 0;
}