格点统计问题(UVALive 3295,UVALive 3720)
来源:互联网 发布:主播就是网络乞丐 编辑:程序博客网 时间:2024/05/22 12:36
题目链接
UVALive 3720
UVALive 3295
大意
第一题是求
我先说说题解,然后再总结其异同.
分析
第一题我们可以这样办:由对称性我们知道,”\”方向的斜线与”/”方向的斜线总数相同,因此只需求一边的就行.我们可以把问题拆分一下,
我们先求出从(0,0)点到(i,j)这个矩形区域内(经过(0,0))的直线条数,设为
由
接下来我们来求(0,0,)->(i,j)区域内总的方案数(ans),递推关系显然成立
由对称性从一个矩形区域左上角到其他点的直线条数显然等于从右下角到其他点的直线条数,而新增贡献就是(i,j)到区域其他点的贡献数,不过要注意,这样做会被重复计数,举个例子(0,0)->(1,1),一定与(0,0)->(2,2)重复.但是这个从副数目到底为多少呢?我们可以想象至少要矩形长宽均扩大两倍才会重复,因此新增贡献为
可以这样想计算到
dp[i][j] :从(0,0)点到(i,j)这个矩形区域内(经过(0,0))的直线条数,设为dp[i][j] ,
因此这些直线必定会包含在
AC code
#include <bits/stdc++.h>using namespace std;typedef long long LL;const int maxn = 1000+10;LL dp[maxn][maxn];LL sum[maxn][maxn];LL comb_3(LL n){ if(n<0)return n; return n*(n-1)/2*(n-2)/3;}void init(){ memset(dp,0,sizeof(dp)); memset(sum,0,sizeof(sum)); for(int i=1 ; i<maxn ; ++i){ for(int j =1 ; j<maxn ; ++j) dp[i][j] = dp[i-1][j]+dp[i][j-1]-dp[i-1][j-1]+(__gcd(i,j)-1); } for(int i=1 ; i<maxn ; ++i) for(int j = 1 ; j<maxn ; ++j) sum[i][j] = sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+dp[i][j];}int n,m;int main(int argc, char const *argv[]) { init(); int kase = 0; while (scanf("%d%d",&n,&m ) && n) { LL ans = comb_3((n+1)*(m+1)); ans-=comb_3(n+1)*(m+1); ans-=comb_3(m+1)*(n+1); ans-=sum[n][m]*2; printf("Case %d: %lld\n",++kase,ans ); } return 0;}
UVALive 3720
这一题是问有多少个格点三角形.
我们很容易想到从反面来解决的思想,用总的三个点的总数来减去共线三点的总数,由于直线很好统计,难得是斜线。同样的,我们采用上面的方法,
设
dp[i][j] :矩形区域内与(0,0)共线的数目.
由前面的内容我们知道,(i,j)加入之后对dp[i][j]的影响为(gcd(i,j)-1)。与(i,j),(0.0)共线的点为(gcd(i,j)+1),选第三个点共(gcd(i,j)+1-2)种情况.
因此
AC code
#include <bits/stdc++.h>using namespace std;typedef long long LL;const int maxn = 1000+10;LL dp[maxn][maxn];LL sum[maxn][maxn];LL comb_3(LL n){ if(n<0)return n; return n*(n-1)/2*(n-2)/3;}void init(){ memset(dp,0,sizeof(dp)); memset(sum,0,sizeof(sum)); for(int i=1 ; i<maxn ; ++i){ for(int j =1 ; j<maxn ; ++j) dp[i][j] = dp[i-1][j]+dp[i][j-1]-dp[i-1][j-1]+(__gcd(i,j)-1); } for(int i=1 ; i<maxn ; ++i) for(int j = 1 ; j<maxn ; ++j) sum[i][j] = sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+dp[i][j];}int n,m;int main(int argc, char const *argv[]) { init(); int kase = 0; while (scanf("%d%d",&n,&m ) && n) { LL ans = comb_3((n+1)*(m+1)); ans-=comb_3(n+1)*(m+1); ans-=comb_3(m+1)*(n+1); ans-=sum[n][m]*2; printf("Case %d: %lld\n",++kase,ans ); } return 0;}
总结
- 对称转化,左上角为参考点与右下角为参考点的转化
- 递归思考.满足和的性质只需考虑(i,j)的加入的影响.
- 简单化子问题.从最终答案递推到需要求解的问题上去.
1 0
- 格点统计问题(UVALive 3295,UVALive 3720)
- UVALive
- UVALive
- UVALive
- UVALive
- UVALive
- UVALive
- UVALive
- UVALive
- UVALive
- UVALive
- UVALive
- UVALive
- UVALive
- UVALive
- UVALive
- UVALive
- UVALive
- 特征选择
- matplotlib python绘图
- 北大 2012 最大上升子序列和 最简真分数
- Java集合框架综述
- eclipse项目导入错误解决
- 格点统计问题(UVALive 3295,UVALive 3720)
- aspose.slides添加文本框代码反编译
- pwnable.kr [Rookiss]
- swoole
- 现有 mtk8735 mtk8163 mtk8127 mtk6735量产方案 核心板方案 mtk系列平台
- Maven实用总结
- 第五届蓝桥杯扑克序列
- Sublime Text 3 安装、使用Package Control
- ubuntu下安装cmake及cmake简单使用