LuoguP3941 将军令
LuoguP3941 将军令
题面
题解
简化问题如果求一个数列上有几段的和为K的倍数,容易想到记录一下前缀和,前缀和在膜K相同则为一组解
考虑二维枚举开始行和结束行,讲其转化为一维的问题
然后开个桶记录一下,注意对于不同的开始行和结束的列到最后要撤销,而不是直接清零
因为K可能很大,清零的话就会消耗太多时间(可以类比CDQ分治撤销树状数组)
最开始直接清零的,T飞了
#include<bits/stdc++.h> using namespace std; #define LL long long inline LL read() { LL f = 1,x = 0; char ch; do { ch = getchar(); if(ch == '-') f=-1; }while(ch<'0'||ch>'9'); do { x = (x<<3) + (x<<1) + ch - '0'; ch = getchar(); }while(ch>='0'&&ch<='9'); return f*x; } const int MAXN = 400 + 5; long long n,m,k; int a[MAXN][MAXN]; long long sum[MAXN][MAXN]; long long ton[1000000 + 10]; long long b[MAXN]; int main() { n=read(),m=read(),k=read(); for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { a[i][j]=read();a[i][j]%=k; } } for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { sum[i][j] = sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+a[i][j]+k; sum[i][j] %= k; // cout<<sum[i][j]<<endl; } } long long ans = 0; for(int i=0;i<n;i++) { for(int j=i+1;j<=n;j++) { ton[0]=1; for(int h=1;h<=m;h++) { b[h] = (sum[j][h]-sum[i][h]+k)%k; ans+=ton[b[h]]; ton[b[h]]++; } for(int h=1;h<=m;h++) ton[b[h]]=0; } } cout<<ans<<endl; }