HDU5110:Alexandra and COS(分块+容斥)

传送门

题意

给出\(n*m\)的矩阵,'X'代表treasure.\(q\)个询问,每次询问(x,y)且频率为d的潜艇能探索到多少财富。探索范围要求abs(x1-x)>=abs(y1-y),也就是左斜方/右斜方\(45°\),而且要求\(max(abs(x1-x),abs(y1-y))\)为d的倍数

分析

本题直接预处理每个点的复杂度为\(O(nmn)\),会TLE,考虑分块,将\(d<\sqrt{n}\)的点进行预处理,复杂度为\(O(nm\sqrt{n})\),其余点直接暴力算,复杂度为\(O(\sqrt{n})\),而预处理的方法我是从sio_five学到的,不同于复杂的状态转移,而,是采用右三角形-左三角形的方法,代码简短,十分精妙

trick

1.有一点需要注意,如果数组下标未与循环相对应,会TLE,与ac相差3+s,有claris的说法,未对应导致物理不连续,进而导致寻址慢,以后要注意

代码

#include <bits/stdc++.h>
using namespace std;

#define ll long long
#define F(i,a,b) for(int i=a;i<=b;++i)
#define R(i,a,b) for(int i=a;i<b;++i)
#define mem(a,b) memset(a,b,sizeof(a))
const int N = 1010;
int n,m,q,x,y,d,B;
int sum[N][N],g[33][N][N],sa[N][33],gl[N][N],gr[N][N];
//sum[i][j]记录第i行的前j个和,sa[i][j]记录前i行间距为j的行和,比如sa[3][2]=sum[3][m]+sum[1][m]
//gl[i][j]记录以(i,j)为边界的左三角形,gr[i][j]记录以(i,j)为边界的右三角形
char s[N][N];
void init()
{
    F(i,1,n)F(j,1,B)
    {
        if(i<j) sa[i][j]=sum[i][m];
        else sa[i][j]=sa[i-j][j]+sum[i][m];
    }
    F(k,1,B)F(i,1,n)F(j,1,m)
    {
        //gl
        if(i<=k||j<=k) gl[i][j]=sum[i][j];
        else gl[i][j]=gl[i-k][j-k]+sum[i][j];
        //gr
        if(i<=k) gr[i][j]=sum[i][j];
        else if(j+k>m) gr[i][j]=sa[i-k][k]+sum[i][j];
        else gr[i][j]=gr[i-k][j+k]+sum[i][j];
        //g
        g[k][i][j]=gr[i][j]-gl[i][j-1];
    }
}
void solve()
{
    int ans=0;
    for(int i=x;i>0;i-=d)
    {
        int l=max(1,y-x+i),r=min(m,y+x-i);
        ans+=sum[i][r]-sum[i][l-1];
    }
    printf("%d\n",ans);
}
int main()
{
    while(scanf("%d %d %d",&n,&m,&q)!=EOF)
    {
        F(i,1,n)
        {
            scanf("%s",s[i]+1);
            F(j,1,m) sum[i][j]=sum[i][j-1]+(s[i][j]=='X');
        }
        B=sqrt(n);
        init();
        while(q--)
        {
            scanf("%d %d %d",&x,&y,&d);
            if(d<=B) printf("%d\n",g[d][x][y]);
            else solve(); 
        }
    }
    return 0;
}
posted @ 2017-06-29 21:28  遗风忘语  阅读(178)  评论(0编辑  收藏  举报