【NOIP2017提高A组模拟8.25】夜莺与玫瑰

Description:

【NOIP2017提高A组模拟8.25】夜莺与玫瑰_第1张图片
1<=T<=10000,1<=n<=4000

题解:

好久没有比赛时切这么恶心的数论题了。

显然的想法是枚举直线的斜率,再把这条直线塞到矩形里,平行于坐标轴的直线特判一下就行了。

枚举斜率就是枚举个i,j,斜率就是j/i,保证(gcd(i, j) =1)就不会重复,斜率为负的是一样的,最后乘一个2即可。

最后一个问题这条直线能塞到多少个地方。可以这么想:这条直线占了一个i*j的矩形,那就有(n-i+1)*(m-j+1)个位置可以塞。

但是这样显然是错的,因为会出现下图这种情况:
【NOIP2017提高A组模拟8.25】夜莺与玫瑰_第2张图片
这两条直线存在于两个不同的矩形中,但是实际上是一条直线。

于是我们会想到枚举矩形的左上角,只要没有矩形以左上角为右下角,那么这个矩形就是合法的。

总结一下可得:
Ans=n1i=1m1j=1[gcd(i,j)=1]nix=1mjy=1[x<=i  or  y<=j]

有gcd,就可以反演了。
为了方便,接下来的n,m都减了1。

=ni=1mj=1d|gcd(i,j)μ(d)ni+1x=1mj+1y=1[x<=i  or  y<=j]
nd=1μ(d)ndi=1mdj=1nid+1x=1mjd+1y=1[x<=id  or  y<=jd]
容斥一下,易得:
=nd=1μ(d)ndi=1mdj=1(nid+1x=1[x<=id](mjd+1))+(mjd+1y=1[y<=jd](nid+1)))(nid+1x=1mjd+1y=1[x<=id  and  y<=jd])
=nd=1μ(d)(ndi=1nid+1x=1[x<=id]mdj=1(mjd+1))+(mdj=1mjd+1y=1[y<=jd]ndi=1(nid+1))((ndi=1nid+1x=1[x<=id](mdj=1mjd+1y=1[y<=jd])

于是暴力枚举d,可以利用等差数列求和快速算出所需的东西。

时间复杂度: O(Tn)

这题是2016计蒜客联想的显示屏校准(困难)的一个部分分,实际上式子还可以化简,一个查询是 O(n) ,在这里不再继续推了。

Code:

#include
#include
#define ll long long
#define fo(i, x, y) for(int i = x; i <= y; i ++)
using namespace std;

const int mo = 1 << 30;

const int N = 4000;
int T, n, m, p[N], mu[N + 5];
bool bz[N + 5];
ll ans;

void Build() {
    mu[1] = 1;
    fo(i, 2, N) {
        if(!bz[i]) p[++ p[0]] = i, mu[i] = -1;
        fo(j, 1, p[0]) {
            int k = i * p[j];
            if(k > N) break;
            bz[k] = 1;
            if(i % p[j] == 0) {
                mu[k] = 0; break;
            }
            mu[k] = -mu[i];
        }
    }
}

int main() {
    Build();
    for(scanf("%d", &T); T; T --) {
        scanf("%d %d", &n, &m);
        if(n > m) swap(n, m);
        n --; m --;
        ans = 0;
        fo(i, 1, n) if(mu[i] != 0)  {
            int sf, sg;

            int c = n / i, c2 = (n + 1) / 2 / i, s;
            sf = (1 + c2) * c2 / 2 * i;
            sf += (n + 1) * (c - c2) - (c2 + 1 + c) * (c - c2) / 2 * i;

            c = m / i, c2 = (m + 1) / 2 / i, s;
            sg = (1 + c2) * c2 / 2 * i;
            sg += (m + 1) * (c - c2) - (c2 + 1 + c) * (c - c2) / 2 * i;

            int s1 = (n + 1) * (n / i) - (n / i) * (n / i + 1) * i / 2;
            int s2 = (m + 1) * (m / i) - (m / i) * (m / i + 1) * i / 2;
            ans += mu[i] * (sf * s2 + sg * s1 - sf * sg);
        }
        ans *= 2; ans += n + m + 2;
        ans = (ans % mo + mo) % mo;
        printf("%lld\n", ans);
    }
}

你可能感兴趣的:(莫比乌斯反演)