P8172

太良心了,搬题人!联考开了 1.5s 随便过。

这个复杂度不太对,需要卡常。

upd:循环节长度的结论是错的,所以跟他爆了。

思路

为方便,交换一下定义,是一个 列的矩阵,列从 开始编号,

考虑状压 ,设 表示已经填满了 行,第 行填了状态 。从 转移到 时,设 表示现在要填 ,第 的部分和第 部分并起来的状态为 。发现如果 都为空, 的三角无用,所以没有后效性。

发现 很像矩阵优化 dp。对于每个 算能否转移到每个 。然后就是有 个第 行点不能是状态 的限制,形如要求做完第 行的状态 逻辑与 ,然后 再或上 继续做,分成 段查询。能做 ,跟暴力同分。

只需要知道可行性用 bitset 优化,预处理出 的幂次,然后查询时用 bitset 维护向量。能做 ,跟暴力同分。

再观察,猜测经过若干个空的行之后会出现长为 的循环节。发现一个状态 经过 个空行之后就能到达所有经过 个空行能到达的状态。所以 ,那只需要维护前 个转移矩阵的 的幂次。能做 ,跟暴力同分。

卡常方面,有值的状态不多,bitset 自带的 _Find_first()_Find_next(i) 函数可以快速跳一个 bitset 为 的位置,常熟大大减小。然后发现瓶颈在于矩阵乘法,不妨只维护 的转移矩阵,稍微增大查询时向量乘法次数。就赢了!

复杂度

code

int n,m,k,l;
map<int,int> mp;
const int maxm=1<<13;
#define vec bitset<maxm>
struct mat{
	vec e[maxm];
	mat(){
		for(int i=0;i<l;i++)e[i].reset();
	}
	mat operator*(const mat&tmp)const{
		mat res;
		for(int i=0;i<l;i++){
			for(int k=e[i]._Find_first();k<l;k=e[i]._Find_next(k)){
				res.e[i]|=tmp.e[k];
			}
		}
		return res;
	}
}pw[30];
vec operator*(vec e,mat tmp){
	vec res;res.reset();
	for(int k=e._Find_first();k<l;k=e._Find_next(k)){
		res|=tmp.e[k];
	}
	return res;
}
vec res;
bitset<maxm> f[maxn+1];
vector<pii> que;
void work(){
	n=read();m=read();k=read();
	for(int i=1;i<=k;i++){
		int u=read(),v=read();
		mp[v]|=(1<<u-1);
	}
	if(n<=6)return sub1::sovle();
	l=1<<n;
	for(int s=0;s<l;s++){
		for(int i=0;i<=n;i++)f[i].reset();f[0][s]=1;
		for(int i=0;i<n;i++){
			for(int t=f[i]._Find_first();t<l;t=f[i]._Find_next(t)){
				if(t&(1<<i)){
					f[i+1][t^(1<<i)]=1;
				}
				else{
					if(i+1<n&&!(t&(1<<i+1))){
						f[i+2][t|(1<<i)]=1;
						f[i+2][t|(1<<i+1)]=1;
					}
					if(i&&!(t&(1<<i-1))){
						f[i+1][t|(1<<i)|(1<<i-1)]=1;
					}
					if(i+1<n&&(t&(1<<i+1))){
						f[i+2][t|(1<<i)]=1;
					}
				}
			}
		}
		pw[0].e[s]=f[n];
	}
	for(auto [p,s]:mp)que.pb({p,s});
	que.pb({m+1,l-1});
	int mx=0,B=6,k=29;
	for(int i=0;i<que.size()-1;i++)mx=max(mx,que[i+1].fi-que[i].fi);
	mx=min(mx,B);k=2;
	for(int i=1;i<=k;i++)pw[i]=pw[i-1]*pw[i-1];
	// return ;
	int lst=0;res[0]=1;
	for(auto[p,s]:que){
		int len=p-1-lst;
		len=min(len,len%B+B);
		while(len>=(1<<k))res=res*pw[k],len-=1<<k;
		for(int i=k;~i;i--)if(len&(1<<i)){
			res=res*pw[i];
		}
		for(int t=res._Find_first();t<l;t=res._Find_next(t)){
			if(s&t)res[t]=0;
		}
		if(!res.count()){puts("NO");return ;}
		for(int t=res._Find_first();t<l;t=res._Find_next(t)){
			res[t]=0,res[s|t]=1;
		}
		lst=p-1;
	}
	puts("YES");
}

upd

关于正解,其实也很脑瘫,你把两个有限制的行间的距离缩为 以内,总行数 ,就不用矩阵快速幂了,直接做原 dp 即可。复杂度