[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 路面修整