• “蔚来杯“2022牛客暑期多校训练营9 F题: Matrix and GCD


    F题: Matrix and GCD

    原题链接:https://ac.nowcoder.com/acm/contest/33194/F

    题目大意

    给定 n × m n\times m n×m 大小的矩阵 M M M [ 1 , n m ] [1,nm] [1,nm] 范围内每个整数只在 M M M 中出现一次。定义子矩阵的权值为所有元素的 G C D GCD GCD ,求 M M M 的所有连续子矩阵的权值和。

    题解

    枚举子矩阵计算 G C D GCD GCD 的时间复杂度为 O ( n 2 m 2 ) O(n^2m^2) O(n2m2) ,显然不现实。
    不妨枚举 G C D GCD GCD ,然后统计符合的子矩阵个数。
    f i f_i fi 表示 G C D GCD GCD i i i 的倍数的子矩阵个数。
    f i f_i fi 的过程可以变为:将 i i i 的倍数变为 1 1 1 ,非 i i i 的倍数变为 0 0 0 ,然后求全 1 1 1 子矩阵个数即可。
    这是一个经典问题,参考洛谷P3400,可以在 O ( n m ) O(nm) O(nm) 的时间复杂度内完成。
    若在每次枚举 i i i 时重构矩阵,复杂度为 O ( n 2 m 2 ) O(n^2m^2) O(n2m2) ,仍不现实。
    但是,我们在每次统计时只需要遍历那些为 1 1 1 的位,则总复杂度可降为 ∑ i = 1 n m ⌊ n m i ⌋ \sum\limits_{i=1}^{nm}\lfloor\frac{nm}{i}\rfloor i=1nminm ,即 O ( n m l o g n m ) O(nmlognm) O(nmlognm) 级别,可以通过此题。
    (实际上在每次遍历时需要对位从左上到右下进行排序,复杂度为 ∑ i = 1 n m ⌊ n m i ⌋ l o g ⌊ n m i ⌋ \sum\limits_{i=1}^{nm}\lfloor\frac{nm}{i}\rfloor log\lfloor\frac{nm}{i}\rfloor i=1nminmloginm ,即 O ( n m l o g 2 n m ) O(nmlog^2nm) O(nmlog2nm) 级别,时限为 4 s 4s 4s ,可行)
    g i g_i gi 表示 G C D GCD GCD 恰好为 i i i 的子矩阵个数。
    根据定义,简单容斥可得:
    g i = f i − ∑ i ∣ j g j g_{i}= f_i-\sum_{i|j}g_j gi=fiijgj

    计算时从大到小枚举保证 i i i 调用的 g j g_j gj 已经被计算过,复杂度为 O ( n m l o g n m ) O(nmlognm) O(nmlognm)

    最终答案:
    a n s = ∑ i = 1 n m g i × i ans=\sum_{i=1}^{nm}g_i\times i ans=i=1nmgi×i

    参考代码

    #include
    using namespace std;
    
    template<class T>inline void read(T&x){
    	char c,last=' ';
    	while(!isdigit(c=getchar()))last=c;
    	x=c^48;
    	while(isdigit(c=getchar()))x=(x<<3)+(x<<1)+(c^48);
    	if(last=='-')x=-x;
    }
    
    #define LL long long
    const int MAXN=1e3+5;
    int n,m;
    int p[MAXN*MAXN];//数对应的位置
    LL f[MAXN*MAXN];
    int h[MAXN*MAXN];
    int c[MAXN*MAXN];
    int stk[MAXN],top;
    
    LL solve(int x){
    	vector<int>v;
    	for(int y=x;y<=n*m;y+=x)v.push_back(p[y]);//枚举所有为x倍数的位置
    	sort(v.begin(),v.end());
    	for(int t=0;t<(int)v.size();++t){
    		int x=(v[t]-1)/m+1,y=(v[t]-1)%m+1;
    		h[(x-1)*m+y]=h[(x-2)*m+y]+1;
    	}
    	LL ret=0,sum;
    	for(int t=0,lastx=0,lasty=0;t<(int)v.size();++t){
    		int x=(v[t]-1)/m+1,y=(v[t]-1)%m+1;
    		c[v[t]]=1;
    		if(x==lastx&&y==lasty+1){//连续的,继续计算
    			while(top&&h[stk[top]]>=h[v[t]]){
    				sum-=h[stk[top]]*c[stk[top]];
    				c[v[t]]+=c[stk[top]];
    				--top;
    			}
    		}
    		else{//不连续,清空
    			top=0;
    			sum=0;
    		}
    		sum+=h[v[t]]*c[v[t]];
    		stk[++top]=v[t];
    		ret+=sum;
    		lastx=x,lasty=y;
    	}
    	for(int t=0;t<(int)v.size();++t){//注意清空数组
    		h[v[t]]=c[v[t]]=0;
    	}
    	return ret;
    }
    
    int main()
    {
    	read(n),read(m);
    	for(int i=1;i<=n;++i){
    		for(int j=1;j<=m;++j){
    			int x;read(x);
    			p[x]=(i-1)*m+j;//保存位置
    		}
    	}
    	for(int i=1;i<=n*m;++i)f[i]=solve(i);
    	LL ans=0;
    	for(int i=n*m;i>=1;--i){
    		for(int j=i+i;j<=n*m;j+=i)f[i]-=f[j];
    		//此处计算完成后f[i]的意义为GCD恰好为i的子矩阵个数(即文中的g[i])
    		ans+=f[i]*i;
    	}
    	cout<<ans<<'\n';
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
  • 相关阅读:
    7-zip
    关于贪心算法的经典问题(算法效率 or 动态规划)
    数据分析9
    PostgreSQL文本搜索(一)——简介
    python函数(2):文件操作
    android WebRtc 视频通话(P2P)
    业务真的需要微服务吗
    10 | apt 常用操作命令
    基于Python+Selenium+Unittest+PO设计模式
    qt-mvvm代码分析
  • 原文地址:https://blog.csdn.net/Spy_Savior/article/details/126367360