BZOJ3515 Evenpaths

版权声明:写得不好,转载请通知一声,还请注明出处,感激不尽 https://blog.csdn.net/As_A_Kid/article/details/87740263

Problem

BZOJ

Solution

看到数据范围,考虑折半搜索。按照拓扑序,把关键点集平分为左右两边来考虑。

如果我们dp出0到点x的路径条数 p x p_x ,点x到1的路径条数 s x s_x ,那么被这个点计数的路径条数显然是 p x × s x p_x\times s_x 。对于一条路径,我们用第一次经过的右半边的关键点(可能没有障碍)来计数,因此顺便把1号点也设为关键点。这样 p x p_x 只和左边障碍点状态有关, s x s_x 只和右边障碍点状态有关。

我们可以暴力枚举左半边的障碍放置情况,然后dp出当前情况下右半边关键点的 p x p_x 。同样地,对右半边处理出 s x s_x 。注意到我们只关心路径条数的奇偶性,所以可以把所有关键点的 p x p_x 模2,再把这些放在一起即可得到01串。

注意到把各个位同时进行模2意义下的乘法,其真值表和按位与相同。那么我们把这些01串的状态用桶存起来,然后拼接就可以用FWT加速。这样在最终局面下,有偶数个1就说明共有偶数条路径,计数即可。

设障碍物个数为 m m ,时间复杂度 O ( ( n + m ) 2 m 2 ) O((n+m)2^\frac m 2)

因为一个细节调了半天感觉药丸。。

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;
}

猜你喜欢

转载自blog.csdn.net/As_A_Kid/article/details/87740263