bzoj3505 [Cqoi2014]数三角形——组合数+容斥
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3505
好题啊好题...好像还曾经出现在什么智力测试卷中来着...当时不会现在还是无法自己推出来...
自己初步的想法是分类成有两点在一条横线上的和三点在不同横线上的;
第一类就是枚举两条横线,枚举有两点的横线上的两个位置,枚举另一点在横线上的位置,再交换两条横线;
也就是 C(n,2) * C(m,2) * m * 2;
第二类就枚举三条横线,再枚举三个位置;
也就是 C(n,3) * m * m * (m-1);
然后发现不一定 m-1,万一前两个点确定的横线在第三条横线上没有整点呢!
枚举? gcd?好像都不行...于是就萎了...
题解是随便选情况减去一条线情况,一条线分横线竖线和斜线;
对于斜线,确定一个点 (0,0),枚举一个(左下角)点 (i,j),计算一个(对角线)点 gcd(i,j)-1;
再把这个矩形平移,也就是 * (n-i) * (m-j);
再分斜线的方向,也就是 * 2;
所以枚举 i , j ,ans -= 2 * (gcd(i,j)-1) * (n-i) *(m-j);
看博客:https://www.cnblogs.com/Var123/p/5377616.html
还有:https://blog.csdn.net/u012288458/article/details/48624859
代码如下:
#include<iostream> #include<cstdio> #include<cstring> using namespace std; typedef long long ll; int const maxn=1e6+5; ll n,m; ll ans; ll c(ll x) { //// c[1][1]=1; // for(ll i=0;i<=n;i++)c[i][0]=1; // for(ll i=2;i<=maxn;i++) // for(int j=1;j<=3&&i>=j;j++) // c[i][j]=c[i-1][j]+c[i-1][j-1]; return x*(x-1)*(x-2)/6;//C(x,3) } ll gcd(ll a,ll b){return a%b?gcd(b,a%b):b;} int main() { scanf("%lld%lld",&n,&m); n++; m++; ans=c(n*m)-c(n)*m-c(m)*n; for(ll i=1;i<n;i++)//从0开始算点 for(ll j=1;j<m;j++) ans-=2*(gcd(i,j)-1)*(n-i)*(m-j); printf("%lld",ans); return 0; }