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;
}

猜你喜欢

转载自www.cnblogs.com/wlzs1432/p/11723506.html