zoukankan      html  css  js  c++  java
  • 【题解】 CF1493F Enchanted Matrix 交互+border+复杂度分析

    Legend

    Link ( extrm{to Codeforces})

    Editorial

    这个询问次数限制非常鬼畜:(3 cdot lfloor log_2 (n+m) floor),而且答案的 ((r,c)) 一定满足 (r|n,c|m) 看起来就跟质因子的指数和和有什么奇怪的关系。

    先来分析一下题目。我们画一个小矩形,然后把它复制若干份平铺,看看能不能发现什么性质。

    Observation 1: 行列是独立的,我们只要求出行和列的方案然后乘起来就行了。

    显然,但证明我还没想到什么好办法。

    于是 2D 问题变成了序列问题,也就是现在有一个长 (n) 的序列,你每次可以询问两个不相交子串,求它的最小周期。

    最小周期?考虑怎么判断一个长度 (k) 是不是 (n) 的周期,根据定义来说,必须这样:

    • (k) 个元素捆在一起,则 (frac{n}{k}) 个元素完全一致。

    正好交互器支持询问元素是否一致,看来这就是正解的方向!但是两个两个问太慢了,怎么办呢?

    你如果对于字符串理论非常熟悉,那么你可能会想到 border 可以用来判定周期,只需要询问一个前缀和后缀是否相等就行了。而对于 border 不相交的情况,我们直接询问就行。但是 border 是有可能相交的,如下图。(我们认为周期长度是 (4)X. 交替仅为美观)

    XXXX....XXXX....XXXX....XXXX
    [______________________]
        [______________________]
    

    我们不能询问相交的区间,怎么办办呢?你可能会想能不能递归判定呢?

    不过这复杂度太高,我们必须要一个常数次询问的做法。

    Observation 2:可以在最多 (2) 次操作的情况下判定一个长度是不是周期。

    下给出构造方法:

    XXXX....XXXX....XXXX....XXXX
    [_____1____]
                [_____2____]
                    [_____3____]
    

    聪明的人类智慧告诉我们,我们可以先问 (1,2),再问 (2,3)。如果 (1=2=3),那么长度 (4) 就可以被判定了。

    等于说我们用 (1),强行比较了原本相交的 (2,3)。我们知道了 (2,3) 这个的周期可以是长度 (4),然后我们又把前缀复制了一份丢在前面,那么长度 (4) 肯定也是周期。这样就完成了判定。

    所以我们可以在最多 (2) 次操作的情况下判定一个长度是不是周期。

    Observation 3:如果长度 (a) 是答案,(b) 也是答案,则 (gcd(a,b)) 一定是答案。

    根据周期的理论,我们可以得出它是真的。

    所以说,我们初始令最短周期是 (n),尝试每次从小到大消除它的一个质因子即可。

    这样复杂度是什么?设当前正在消除的质因子是 (P)

    我们也就要检测 (frac{n}{P}) 是否是周期。

    • 如果是,则 (n gets frac{n}{P})
    • 如果不是,换下一个质因子。

    Observation 4:上述算法时间查询次数不超过 (lfloor log_3 16 log_2 n floor+4)

    我们发现 (P=2) 时只需要 (1) 次查询,(P>3) 时需要 (2) 次查询。

    所以说我们结束这个算法至多需要 (2lfloor log_3 n floor) 次询问。如果假设题目中的 (n,m) 同阶,则需要 (4lfloor log_3 n floor) 次询问。

    经过一番高中必修一(换底公式)数学推导,我们得到 (4lfloor log_3 n floor le log_3 16 log_2 n)

    <这里本该有张图>

    其中红色的代表题目中的限制 (3 lfloor log_2 n floor+3)。不难发现,只有当 (n=1,2,3) 的时候我们的算法可能会挂,但是发现由于我们的不等式是做了过剩估计的,其实并不会挂。

    (至于怎么证 (n e m) 的呢,我不会)

    Code

    #include <bits/stdc++.h>
    
    #define debug(...) fprintf(stderr ,__VA_ARGS__)
    #define __FILE(x)
    	freopen(#x".in" ,"r" ,stdin);
    	freopen(#x".out" ,"w" ,stdout)
    #define LL long long
    
    const int MX = 1e3 + 23;
    const LL MOD = 998244353;
    
    int read(){
    	char k = getchar(); int x = 0;
    	while(k < '0' || k > '9') k = getchar();
    	while(k >= '0' && k <= '9') x = x * 10 + k - '0' ,k = getchar();
    	return x;
    }
    
    int qpow(int a ,int b){
    	int r = 1;
    	for(int i = 1 ; i <= b ; ++i) r *= a;
    	return r;
    }
    
    int qc;
    int ask(int h ,int w ,int x1 ,int y1 ,int x2 ,int y2){
    	++qc;
    	//	return 1;
    	printf("? %d %d %d %d %d %d
    " ,h ,w ,x1 ,y1 ,x2 ,y2); fflush(stdout);
    	return read();
    }
    
    int n ,m;
    int getit(int a ,int x){
    	int r = 0;
    	for(int i = a ; i <= x ; i += a)
    		r += x % i == 0;
    	return r;
    }
    
    int main(){
    	n = read() ,m = read();
    	int a1 = m;
    	for(int i = 2 ; i <= m ; ++i){
    		int is_prime = true;
    		for(int j = 2 ; j * j <= i ; ++j){
    			if(i % j == 0) is_prime = false;
    		}
    		if(!is_prime) continue;
    		while(a1 % i == 0){
    			int tmp = a1 / i; // 看看长度为 tmp 行不行
    			// 分成 i 段了
    			int ok = 0;
    #define id(x) (((x) - 1) * tmp + 1)
    			if(i == 2) ok = ask(n ,tmp ,1 ,id(1) ,1 ,id(2));
    			else ok = ask(n ,i / 2 * tmp ,1 ,id(1) ,1 ,id(i / 2 + 1))
    				   && ask(n ,i / 2 * tmp ,1 ,id(1) ,1 ,id(i / 2 + 2));
    			if(ok){
    				a1 /= i;
    			}
    			else{
    				break;
    			}
    		}
    	}
    	int a2 = n;
    	for(int i = 2 ; i <= n ; ++i){
    		int is_prime = true;
    		for(int j = 2 ; j * j <= i ; ++j){
    			if(i % j == 0) is_prime = false;
    		}
    		if(!is_prime) continue;
    		while(a2 % i == 0){
    			int tmp = a2 / i; // 看看长度为 tmp 行不行
    			// 分成 i 段了
    			int ok = 0;
    #define id(x) (((x) - 1) * tmp + 1)
    			if(i == 2) ok = ask(tmp ,m ,id(1) ,1 ,id(2) ,1);
    			else ok = ask(i / 2 * tmp ,m ,id(1) ,1 ,id(i / 2 + 1) ,1)
    				   && ask(i / 2 * tmp ,m ,id(1) ,1 ,id(i / 2 + 2) ,1);
    			if(ok){
    				a2 /= i;
    			}
    			else{
    				break;
    			}
    		}
    	}
    	printf("! %d
    " ,getit(a1 ,m) * getit(a2 ,n)); fflush(stdout);
    	debug("query %d times %d %d
    " ,qc ,a2 ,a1);
    	return 0;
    }
    
  • 相关阅读:
    产品方法论
    elastic search语句
    计算机科学发展的动力
    理论计算机科学学习网站
    算法学习 howto
    人工智能和机器学习 AI&ML howto
    Deep Learning 和 Knowledge Graph howto
    LISP语言学习资源
    Turing Year 2012
    如何做好计算机科学研究
  • 原文地址:https://www.cnblogs.com/imakf/p/14550320.html
Copyright © 2011-2022 走看看