CF1493F Enchanted Matrix
“天无二日,@flying2018 是我们永远的红太阳。”$\ \ \ $ ——鲁迅
题意
对于一个 \(n\times m\) 的矩形,将其行 \(\frac{n}{r}\) 等分,列 \(\frac{m}{w}\) 等分(\(r,w\) 就表示子矩阵的长和宽),如果这样分出来的所有子矩形都相同,那么称 \((r,w)\) 为一组合法的二元组。
你每次可以询问两个不相交的矩形是否相等,请在 \(3\cdot \lfloor\log_2 (n+m)\rfloor\) 次询问内确定合法解的个数。
Solution
Part 1 行列独立
这里是一个很显然的结论:\((r,w)\) 的 真假 可以转化成 \((r,m)\&(n,w)\)。
前推后显然,后推前也很好证。
那么接下来我们就把问题转为了一维。
Part 2 单维内寻找最短循环节
就拿行这一维举例。
这里是一个字符串题的常用套路:对于这种分多段全等的问题,只需要找它的最短循环节即可。
我们一开始已知 \(n\) 是一个合法解,\(n=\prod p_i^{k_i}\),表示成这样的形式之后,由于最短循环节 \(r'|n\),所以可以将质因子一个一个试除,来得到 \(r'\)。
Part 3 询问策略
如何检测一个长度是否是合法的?
显然按照刚刚的检查策略的话,我们对于一次检测长度 \(r'\) 是否合法的时候,只有最多 \(3\) 次机会。
以下称分成的段数 \(\frac{n}{r'}\) 为 \(L\)。
这里又是一个字符串题的常见套路:你此时把每按检测长度 \(r'\) 分好的每一段 看作一个元素,然后就是一个询问是否全部相等的问题。
-
对于只有两个元素的话,直接询问即可。
-
大于等于三个元素的话,此时 \(L\) 因为是大于 \(2\) 的质因数,所以必定为奇。考虑如果没有不能重叠这个限制,我们可以直接问 \([1,L-1],[2,L]\) 是否相等。对于这个不能重叠的限制的话,我们也可以利用这个原理,不过是找到另一个 公共串 来进行询问。具体的,对于我们想询问是否全等的串 \([l,r]\),我们可以将 \([l,r-1]\) 和 \([l+1,r]\) 分别与 \([1,r-l]\) 进行询问,这也就要求 \(r<2l\)。我们可以利用这个原理来查询 \([\frac{L+1}{2},L]\) 是否完全相等,并且也可以知道 \([1,\frac{L-1}{2}]\) 与 \([\frac{L+1}{2},L-1]\) 相等,从而推出所有元素相等。
Code
#include <queue>
#include <vector>
#include <string>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#define LL long long
using namespace std;
inline int rin()
{
int s=0;
bool bj=false;
char c=getchar();
for(;(c>'9'||c<'0')&&c!='-';c=getchar());
if(c=='-')bj=true,c=getchar();
for(;c>='0'&&c<='9';c=getchar())s=(s<<1)+(s<<3)+(c^'0');
if(bj)return -s;
return s;
}
inline void jh(int &x,int &y){if(x^y)x^=y^=x^=y;return;}
inline void jh(LL &x,LL &y){if(x^y)x^=y^=x^=y;return;}
const int N=1e5+3;
int n,m,nm;
bool tag;
int sk[N];
inline int find(int L,int R,int x){if(tag)printf("? %d %d %d %d %d %d\n",n,x,1,L,1,R);else printf("? %d %d %d %d %d %d\n",x,m,L,1,R,1);fflush(stdout);return rin();}
inline int que(int L,int R)
{
if(L==2)return find(1,R+1,R);
if(L==3)return find(1,R+1,R)&&find(1,(R<<1)+1,R);
int mid=R*(L>>1);return find(1,mid+R+1,mid)&&find(1,mid+1,mid);
}
inline void def(LL ans){printf("! %lld\n",ans);fflush(stdout);return;}
inline void init()
{
n=rin();m=rin();nm=max(n,m);
for(int i=1;i<=nm;i++)sk[i]=i;
for(int i=2;i<=nm;i++)for(int j=i<<1;j<=nm;j+=i)sk[j]=min(i,sk[j]);
return;
}
inline void work()
{
init();
int n_R=n,m_R=m;
int cutt_n=0,cutt_m=0;
tag=false;for(int i=n;i>1;i/=sk[i])if(que(sk[i],n_R/sk[i]))n_R/=sk[i];
tag=true;for(int i=m;i>1;i/=sk[i])if(que(sk[i],m_R/sk[i]))m_R/=sk[i];
int n_L=n,m_L=m;n_L/=n_R;m_L/=m_R;
for(int i=1;i<=n_L;i++)if(!(n_L%i))cutt_n++;
for(int i=1;i<=m_L;i++)if(!(m_L%i))cutt_m++;
def(1LL*cutt_n*cutt_m);
return;
}
int main()
{
work();
return 0;
}