hdu_5110_Alexandra and COS(DP+分块思想)
题目连接:hdu_5110_Alexandra and COS
题意:
给你一个图,X代表宝藏,然后有一个船,它的声纳的频率为D,定船到宝藏的距离为Dis=max(abs(x1-x2),abs(y1-y2)),如果D是Dis的约数并且宝藏在船的上方开角45°,那么这个船就能探测到这个宝藏,现在给你q个询问,每一个询问有一个位置x,y和一个声纳的频率D,问这个船能探测到多少宝藏
题解:
因为是45°角,所以Dis实际就是abs(y1-y2),然后我们可以对每一个D,DP它45°开角满足条件的前缀和,不过对每一个D都这样做,那肯定超时,仔细想想,D越大,直接暴力搜肯定会比DP来的快,因为直接暴力我们可以每次跳D格来找,所以这里我们就要分块,一般分为sqr=sqrt(n),不过这里我亲测出数据好像当sqr=3时,跑的速度更快,然后我就直接分为D>3和D<=3来做
这里DP的方程为:设dp[i][j][k]为第i行,第j列,声纳频率为k的开角45°满足条件的前缀和,状态转移方程为dp[i][j][k]=dp[i-k][j-k][k]+dp[i-k][j+k][k]-dp[i-2*k][j][k]+第(i-k)行中[j-k,j+k]满足条件的点
(没装画图软件,画的有点丑,将就看)下面以D为2时举例,假设我们要查询x=5,y=3这个点,红色的代表满足条件的点,蓝色的线代表范围,我们可以看到粉圈内的点加了两次,所以要减掉,对应的范围就是dp[i-2*k][j][k]
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #define F(i,a,b) for(int i=a;i<=b;++i) 5 using namespace std; 6 7 const int N=1007; 8 int dp[N][N][4],g[N][N],n,m,q; 9 char in[N][N]; 10 11 int main(){ 12 while(~scanf("%d%d%d",&n,&m,&q)){ 13 F(i,1,n)scanf("%s",in[i]+1); 14 F(i,1,n)F(j,1,m)g[i][j]=g[i][j-1]+(in[i][j]=='X'); 15 for(int i=1,*p,kk;i<=n;++i)F(j,1,m)F(k,1,3){ 16 p=&dp[i][j][k],*p=(in[i][j]=='X'),kk=k<<1; 17 if(i>k){ 18 if(j>k)*p+=dp[i-k][j-k][k]+g[i-k][j]-g[i-k][j-k]; 19 else{ 20 *p+=g[i-k][j]; 21 if(i>kk)*p+=dp[i-kk][j][k]+g[i-kk][j-1]; 22 } 23 if(j+k<=m)*p+=dp[i-k][j+k][k]+g[i-k][j+k-1]-g[i-k][j]; 24 else{ 25 *p+=g[i-k][m]-g[i-k][j]; 26 if(i>kk)*p+=dp[i-kk][j][k]+g[i-kk][m]-g[i-kk][j]; 27 } 28 if(i>kk)*p-=dp[i-kk][j][k]; 29 } 30 } 31 for(int i=1,c,r,w,ret;i<=q;i++){ 32 scanf("%d%d%d",&r,&c,&w); 33 if(w>3){ 34 ret=0; 35 for(int j=r,lf=c,rt=c;j>0;j-=w,lf=max(lf-w,1),rt=min(m,rt+w)) 36 ret+=g[j][rt]-g[j][lf-1]; 37 printf("%d\n",ret); 38 }else printf("%d\n",dp[r][c][w]); 39 } 40 } 41 return 0; 42 }