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;
}
posted @ 2021-03-08 16:09  zjjws  阅读(146)  评论(0编辑  收藏  举报