给出的题解不错,所以不写得太详细了。
谁不会60分的那真是没办法了。
1 #include<cstdio> 2 int x[405][405],n,m,k,ans; 3 int main(){ 4 scanf("%d%d%d",&n,&m,&k); 5 for(int i=1;i<=n;++i)for(int j=1,y;j<=m;++j)scanf("%d",&y),x[i][j]=(x[i-1][j]+x[i][j-1]-x[i-1][j-1]+y)%k; 6 for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)for(int p=0;p<i;++p)for(int q=0;q<j;++q)if((x[i][j]-x[p][j]-x[i][q]+x[p][q])%k==0)ans++; 7 printf("%d ",ans); 8 }
看数据范围,有不少测试点m=2。这有什么用?(肯定和同样矩阵大小而mn均匀的点不一样啦)
永远不要忽视测试点的提示作用。NOIP数据范围就很全。
对于只有两行的矩阵,首先我们只选出单独一行的2种,处理它。
接下来就只有两行一起选的问题了。
扩展到更多行,我们可以发现所有行上的选法是m2的。
而如果把每一行的同一列上的数加起来得到新的一行,这就变成了单行上的问题了。
现在我们只需要O(n)求出单行。
刚开始我感觉不可做。但是实际上,这一行的前i位前缀和为sum[i],那么区间l~r能被k整除当且仅当(sum[r]-sum[l-1])%k==0
证明很简单。因为这一段能整除的话,在加上其它的一段值就是后者的值。
所以我们要找满足条件的l-1。也就是给出sum[r]问有多少sum[l-1]满足条件。
开一堆桶buc,buc[p]表示sum[i]%k==p对应的i的个数。每次以r为区间右端点累加答案时直接加上buc[sum[r]]即可。
而且如果一个sum值本身就是k的倍数,那么也要ans++。
1 #include<cstdio> 2 int x[405][405],n,m,mod,buc[1000005],l[405];long long ans; 3 int main(){ 4 scanf("%d%d%d",&n,&m,&mod); 5 for(int i=1;i<=n;++i)for(int j=1,y;j<=m;++j)scanf("%d",&x[i][j]),(x[i][j]+=x[i][j-1])%=mod; 6 for(int i=1;i<=n;++i){ 7 for(int j=1;j<=m;++j)l[j]=0; 8 for(int j=i;j<=n;++j){ 9 for(int k=1;k<=m;++k)(l[k]+=x[j][k])%=mod,ans+=buc[l[k]]+(l[k]?0:1),buc[l[k]]++; 10 for(int k=1;k<=m;++k)buc[l[k]]--; 11 } 12 } 13 printf("%lld ",ans); 14 }