UVALive 3720
UVALive 3295
第一题是求 nXm 的格点中有多少条斜线(至少经过两个点).第二个问题求的是有多少个各点组成的三角形.
我先说说题解,然后再总结其异同.
第一题我们可以这样办:由对称性我们知道,”\”方向的斜线与”/”方向的斜线总数相同,因此只需求一边的就行.我们可以把问题拆分一下,
我们先求出从(0,0)点到(i,j)这个矩形区域内(经过(0,0))的直线条数,设为 dp[i][j] ,这是能够递推的,
由 dp[i][j] 的定义,我们知道只需要判断(i,j)是否能与(0,0)构成一条新的直线即可.因此由简单的几何思想我们知道只需要判,断 gcd(i,j)==1 ,因此
dp[i][j] :从(0,0)点到(i,j)这个矩形区域内(经过(0,0))的直线条数,设为 dp[i][j] ,
因此这些直线必定会包含在 (dp[i][j]) 中.
#include
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 ; ifor(int j =1 ; j1][j]+dp[i][j-1]-dp[i-1][j-1]+(__gcd(i,j)-1);
}
for(int i=1 ; ifor(int j = 1 ; j1][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;
}
这一题是问有多少个格点三角形.
我们很容易想到从反面来解决的思想,用总的三个点的总数来减去共线三点的总数,由于直线很好统计,难得是斜线。同样的,我们采用上面的方法, ans[i][j] 表示矩形区域 (0,0)->(i.j)内的共线的三点数.由前面的方法我们知道
dp[i][j] :矩形区域内与(0,0)共线的数目.
#include
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 ; ifor(int j =1 ; j1][j]+dp[i][j-1]-dp[i-1][j-1]+(__gcd(i,j)-1);
}
for(int i=1 ; ifor(int j = 1 ; j1][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;
}