[USACO21FEB] Count the Cows G 题解

Posted 栾竹清影

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[USACO21FEB] Count the Cows G 题解相关的知识,希望对你有一定的参考价值。

题意转化

题目的要求看起来很复杂,但是,如果我们尝试打一个表,输出前面矩阵放置奶牛的情况,我们不难发现,题目的矩阵就是如下矩阵:

1 0 1

0 1 0

1 0 1

对于这个基本矩阵,按照如上的摆放方式生成更大的矩阵,依次类推。

于是题目就转化为,对于这个递归式的矩阵,求出 \\((x,y)\\)\\((x+d,y+d)\\) 对角线上 \\(1\\) 的个数。

注:容易发现,上方图形沿对角线对称,所以我们假设 \\(x\\leq y\\)

问题转化

看到有 \\(y=0\\) 的部分分,于是考虑将所有数据转化成 \\(y=0\\) 的情况。

\\(f_{i,j,n}\\) 表示从 \\((0,0)\\)\\((i,j)\\) 对角线上 \\(1\\) 的个数,其中, \\(n\\) 是左上角为原点、包含整条对角线的正方形的边长。

那么,\\((x,y)\\)\\((x+d,y+d)\\) 对角线上 \\(1\\) 的个数就可以转化为 \\(f_{x+d,y+d,n}-f_{x-1,y-1,n\'}\\)(前缀和思想)。

于是,现在问题就转化为求 \\(f_{i,j,n}\\) 的值了。

问题求解

以下图为例:

设现在要求的是 \\(f_{x,y,n}\\),如图是边长为 \\(n\\)、左上角为 \\((0,0)\\) 的正方形,编号为子矩阵编号。

另外地,为了表示简便,再设 \\(g_{i,n}\\) 表示从 \\((0,i)\\) 出发的对角线在左上角为 \\((0,0)\\)、边长为 \\(n\\) 的正方形中 \\(1\\) 的个数。

\\(z=x-y\\)(即线段延长至 \\(y=0\\) 时的 \\(x\\) 坐标),\\(m=n/3\\)(即子矩阵边长)。

在以下情况下,\\(f_{x,y,n}\\) 分别等于:

  • 线段从矩阵 \\(1\\) 出发(\\(z<m\\)

    • 到矩阵 \\(1\\) 结束(\\(x<m,y<m\\)):\\(f_{x,y,m}\\)

    • 到矩阵 \\(4\\) 结束(\\(m\\leq x<2m,y<m\\)):\\(g_{z,m}\\)

    • 到矩阵 \\(5\\) 结束(\\(m\\leq x< 2m,m\\leq y<2m\\)):\\(f_{x-m,y-m,m}+g_{z,m}\\)

    • 到矩阵 \\(8\\) 结束(\\(x≥2m,m\\leq y< 2m\\)):\\(2\\times g_{z,m}\\)

    • 到矩阵 \\(9\\) 结束(\\(x≥2m,y≥2m\\)):\\(2\\times g_{z,m}+f_{x-2m,y-2m,m}\\)

  • 线段从矩阵 \\(4\\) 出发 (\\(m\\leq z<2m\\)

    • 到矩阵 \\(4\\) 结束(\\(m\\leq x<2m,y<m\\)):\\(0\\)

    • 到矩阵 \\(8\\) 结束(\\(x≥2m,m\\leq y<2m\\)

      • 未经过矩阵 \\(7\\)\\(z=m\\)):\\(0\\)
      • 经过矩阵 \\(7\\)\\(m<z<2m\\)):\\(g_{2m-z,m}\\)
    • 到矩阵 \\(7\\) 结束 (\\(x≥2m,y<m\\)):\\(f_{x-2m,y,m}\\)

  • 线段从矩阵 \\(7\\) 出发(\\(z≥2m\\)

    • 所有情况都与从矩阵 \\(1\\) 出发对称,返回 \\(f_{x-2m,y,m}\\)

(由于 \\(y\\leq x\\),所以 \\(2,3,6\\) 号矩阵不可能经过)

边界条件:当 \\(n=3\\) 时特判答案。


到这里,这道题基本就做完了,唯一的问题就是 \\(g\\) 的转移。其实,由于对角线一定穿过对应正方形,所以,分别从 \\(1,4,7\\) 号矩阵出发的对角线,一定会分别在 \\(9,8,7\\) 号矩阵结束。于是就可以按照 \\(f\\) 数组转移中的三种情况来转移 \\(g\\) 数组了。

代码

具体看注释趴

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=41;
inline ll read()
{
    register ll x=0;
    register char c=getchar();
    for(;!(c>=\'0\'&&c<=\'9\');c=getchar());
    for(;c>=\'0\'&&c<=\'9\';c=getchar())
        x=(x<<1)+(x<<3)+c-\'0\';
    return x;
}
const int biao[3][3]={{1,0,1},
					  {0,2,0},
					  {1,0,3}};
//n=3 时的特判
int q;
ll G(ll z,ll n)//g 的转移
{
	register ll m=n/3;
	if(n==3) 
		return biao[2][2-z];
  //边界条件(注意,本题都是从 0 计数)
	if(z<m)
		return 3*G(z,m);
	else if(z==m)
		return 0;
	else if(z<(m<<1))
		return G(m*2-z,m);
	else
		return G(z-m*2,m);
  //按照方程分情况转移
}
ll F(ll x,ll y,ll n)
{
	if(n==3)
		return biao[x][y];
	if(y>x) 
		swap(x,y);
	register ll m=n/3,z=x-y;
	if(z<m)
	{
		if(x<m&&y<m)
			return F(x,y,m);
		if(y<m&&x>=m&&x<2*m)
			return G(z,m);
		if(x>=m&&x<2*m&&y>=m&&y<2*m)
			return G(z,m)+F(x-m,y-m,m);
		if(x>=2*m&&y>=m&&y<2*m)
			return G(z,m)*2;
		if(x>=2*m&&y>=2*m)
			return G(z,m)*2+F(x-m*2,y-m*2,m);
	}
	if(z>=m&&z<2*m)
	{
		if(x<2*m&&x>=m&&y<m)
			return 0;
		if(y>=m&&y<2*m&&x>=2*m)
		{
			if(z==m)
				return 0;
			if(z>m&&z<2*m)
			    return G(2*m-z,m);
		}
		if(x>=2*m&&y<m)
			return F(x-2*m,y,m);
	}
	if(z>=2*m)
		return F(x-2*m,y,m);
 	//按照方程分情况转移
}
ll Work(ll x,ll y)
{
	if(x<0||y<0) return 0;
	if(y>x) swap(x,y);
	register ll sum=1;
	while(sum<=x)
		sum*=3;//取 n 的值(第一个大于 max{x,y} 的3的整数次幂)
	return F(x,y,sum); 
}
int main()
{
	register ll d,x,y;
	q=read();
	while(q--)
	{
		d=read(),x=read(),y=read();
		printf("%lld\\n",Work(x+d,y+d)-Work(x-1,y-1));
	}
    return 0;
}

Tipes:为了易懂,本代码会稍有些冗长。大家可以自行优化~

以上是关于[USACO21FEB] Count the Cows G 题解的主要内容,如果未能解决你的问题,请参考以下文章

2021.8.19提高B组模拟9T2 + P7412 [USACO21FEB] Year of the Cow (贪心)

洛谷 P3663 [USACO17FEB]Why Did the Cow Cross the Road III S

洛谷P2875 [USACO07FEB]牛的词汇The Cow Lexicon

bzoj1592 [Usaco2008 Feb]Making the Grade 路面修整

[BZOJ 1652][USACO 06FEB]Treats for the Cows 题解(区间DP)

[BZOJ1592][Usaco2008 Feb]Making the Grade 路面修整