格点统计问题(UVALive 3295,UVALive 3720)
2017-03-23 22:46
411 查看
题目链接
UVALive 3720UVALive 3295
大意
第一题是求 nXm 的格点中有多少条斜线(至少经过两个点).第二个问题求的是有多少个各点组成的三角形.我先说说题解,然后再总结其异同.
分析
第一题我们可以这样办:由对称性我们知道,”\”方向的斜线与”/”方向的斜线总数相同,因此只需求一边的就行.我们可以把问题拆分一下,我们先求出从(0,0)点到(i,j)这个矩形区域内(经过(0,0))的直线条数,设为 dp[i][j],这是能够递推的,
dp[i][j]=dp[i−1][j]+dp[i][j−1]−dp[i−1][j−1]+(i,j)对答案的贡献
由dp[i][j]的定义,我们知道只需要判断(i,j)是否能与(0,0)构成一条新的直线即可.因此由简单的几何思想我们知道只需要判,断gcd(i,j)==1,因此
dp[i][j]=dp[i−1][j]+dp[i][j−1]−dp[i−1][j−1]+(gcd(i,j)==1)
接下来我们来求(0,0,)->(i,j)区域内总的方案数(ans),递推关系显然成立
sum[i][j]=sum[i−1][j]+sum[i][j−1]−sum[i−1][j−1]+(i,j)对答案的新增贡献
由对称性从一个矩形区域左上角到其他点的直线条数显然等于从右下角到其他点的直线条数,而新增贡献就是(i,j)到区域其他点的贡献数,不过要注意,这样做会被重复计数,举个例子(0,0)->(1,1),一定与(0,0)->(2,2)重复.但是这个从副数目到底为多少呢?我们可以想象至少要矩形长宽均扩大两倍才会重复,因此新增贡献为
dp[i][j]−dp[i/2][j/2]
可以这样想计算到(sum[i][j])的时候 dp[i/2][j/2]肯定已经加过了,而由我们之前的定义
dp[i][j] :从(0,0)点到(i,j)这个矩形区域内(经过(0,0))的直线条数,设为 dp[i][j],
因此这些直线必定会包含在 (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 [m]*2; printf("Case %d: %lld\n",++kase,ans ); } return 0; }
UVALive 3720
这一题是问有多少个格点三角形.我们很容易想到从反面来解决的思想,用总的三个点的总数来减去共线三点的总数,由于直线很好统计,难得是斜线。同样的,我们采用上面的方法,ans[i][j]表示矩形区域 (0,0)->(i.j)内的共线的三点数.由前面的方法我们知道
ans[i][j]=ans[i−1][j]+ans[i][j−1]−ans[i−1][j−1]+(与(i,j)共线的组数)
设
dp[i][j] :矩形区域内与(0,0)共线的数目.
dp[i][j]=dp[i−1][j]+dp[i][j−1]−dp[i−1][j−1]+gcd(i,j)−1
由前面的内容我们知道,(i,j)加入之后对dp[i][j]的影响为(gcd(i,j)-1)。与(i,j),(0.0)共线的点为(gcd(i,j)+1),选第三个点共(gcd(i,j)+1-2)种情况.
因此
ans[i][j]=ans[i−1][j]+ans[i][j−1]−ans[i−1][j−1]+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 [m]*2; printf("Case %d: %lld\n",++kase,ans ); } return 0; }
总结
对称转化,左上角为参考点与右下角为参考点的转化递归思考.满足和的性质只需考虑(i,j)的加入的影响.
简单化子问题.从最终答案递推到需要求解的问题上去.
相关文章推荐
- UVALive 2995 Image Is Everything 策略问题
- UVAlive 2326 Moving Tables(贪心 + 区间问题)
- UVALIVE 2519 Radar Installation 区间选点问题
- UVaLive 4847 - Binary Search Tree (与BST有关的计数问题)
- uva 11139 格点计数问题
- UVALive 3026 Period KMP 失配函数处理周期的问题
- UVa 1225 / UVALive 3996 Digit Counting 数数字(字符统计)
- UVA Live 3211飞机调度问题-二分+2-SAT
- UVALive 3295 Counting Triangles(组合计数)
- UVALive - 2519 Radar Installation(区间选点问题)
- UVAlive 2519 Radar Installation (区间选点问题)
- uvalive 2326 - Moving Tables(区间覆盖问题)
- uvalive 2519 - Radar Installation(区间选点问题)
- UVALive 3211 2-SAT问题
- UVALIVE 5792 Trie+统计
- UVAlive 2326 Moving Tables(贪心 + 区间问题)
- uvalive 2326 - Moving Tables(区间覆盖问题)
- UVALive 2519 Radar Installation 雷达扫描 区间选点问题
- UVALive 6263 The Dragon and the knights --统计,直线分平面
- UVALive 6665 Dragon&#226;s Cruller --BFS,类八数码问题