跳转至

251004 模拟赛

T1

只需注意到 \(7\times 11\times 13=1001\),然后简单分讨或者暴力枚举 27 种解(为什么全场只有我一个人没用分讨的)即可。

不开 long long 挂成 \(50\)

T2

给定两个整数三元组 \(a,b\),一次操作你可以选择三个数中的某几个数,将他们乘以一个整数或者加上一个整数,问你最少操作多少次可以把 \(a\) 变成 \(b\)

\(|v|\le 10^9\)

暴力分讨即可。注意到答案不超过 \(3\),其中 \(0,1\) 都是好判的。对于 \(2\),先把一个数字相等的情况判掉;分讨两次操作操作的数字是否具有包含关系:若不交,那么枚举操作一个数字,然后对剩下的部分 check1(判断一次能否成功)。若交叉,发现只有很少的情况(一个数字被操作两遍,两个数字被操作一遍),在外面枚举全排列,里面枚举两次操作即可。若包含,先判掉重合的情况;若乘法包含加法,那么加法一定可以放在后面执行,乘法操作三个数,因此可以知道乘数;若加法包含乘法,由于乘法至少也至多对两个数字操作,因此乘数容易得到。

Code

T3

初始给你 \(n\) 个区间 \([l_i,r_i]\),有 \(q\) 次询问,每次询问给你 \(m\) 个点 \(p_{1\sim m}\),问你有多少个区间满足其内部恰好包含奇数个点。

\(n,q,l_i,r_i\le 5\times 10^5,~\sum m\le n\)

\(m\) 根号分治,设阈值为 \(B\),对于 \(m\le B\),发现合法区间形如 \(m^2\) 组限制,由于 \(\sum m^2\)\(nB\) 级别的,因此直接暴力二维数点,用分块平衡即可。对于 \(m>B\),这样的询问不超过 \(\frac nB\) 个,每次询问直接暴力 \(O(n)\) 扫一遍。时间复杂度 \(O(n\sqrt n)\)

也可以用别的方法。考虑一个区间对哪些询问有贡献。虽然看着没什么前途,但是如果变化量较小的话还是有办法维护的。用莫队维护,那么上面的东西是容易求得的,但考虑如何合并各区间的贡献。发现莫队扩展的过程形如单点 flip,在 \(0\to 1\) 时贡献提前计算,减掉区间左端点;在 \(1\to 0\) 时再加上区间右端点即可。时间复杂度 \(O(n\sqrt n)\)。虽然跑得没有他们快,但是空间小的离谱(\(13.2\)M)。

Code

T4

给定 \(n\times m\) 的网格图(坐标从 \((1,1)\)\((n,m)\)),问所有顶点在整点上的三角形,轮廓上整点数量之和是多少。

\(n,m\le 10^6,~T\le 5\)

不妨设 \(m\le n\)

从点或者三角形统计贡献不好做,考虑统计一条边的贡献。对于一条横纵坐标差分别为 \((\Delta x,\Delta y)\) 的边,其上有 \(\gcd(\Delta x,\Delta y)\) 个整点(两个端点只计算一个),而第三个点可以取平面内任意不共线的点,可以取 \(nm\) 个点之后减去共线的情况。共线的情况也是简单的,一个退化成线段的三角形上有 \(2\gcd(\Delta x,\Delta y)\) 个整点,第三个点又可以取所有整点,而取在两个端点上的情况只会把三角形上的其他整点统计一遍,因此加起来也是两遍,正好又是 \(\gcd(x,y)\) 遍。可以列出答案的式子:

\[ \begin{align*} ans1&=\sum_{i=1}^{n}i(n-i)nm(m-1)+\sum_{i=1}^{m}i(m-i)nm(n-1)\\ ans2&=2nm\sum_{x=1}^{n-1}\sum_{y=1}^{m-1}\gcd(x,y)(n-x)(m-y)\\ ans3&=4\sum_{x=1}^{n-1}\sum_{y=1}^{m-1}\gcd(x,y)^2(n-x)(m-y)\\ \end{align*} \]

答案就是 \(ans1+ans2-ans3\)。其中 \(ans2,ans3\) 乘以 \(2\) 是因为线段有两种倾斜方向。

\((n-x)(m-y)\) 拆成 \(4\) 项,就变成 P3768 简单的数学题 了,但是由于 \(n,m\) 不等,因此要用莫反。枚举 \(\gcd\),然后莫反:

\[ \begin{align*} \sum_{d_1=1}^{m}d_1\sum_{x=1}^{\lfloor\frac{n}{d_1}\rfloor}\sum_{y=1}^{\lfloor\frac{m}{d_1}\rfloor}(d_1x)(d_1y) [\gcd(x,y)=1]&=\sum_{d_1=1}^{m}(d_1)^3\sum_{x=1}^{\lfloor\frac{n}{d_1}\rfloor}\sum_{y=1}^{\lfloor\frac{m}{d_1}\rfloor}xy[\gcd(x,y)=1]\\ &=\sum_{d_1=1}^{m}(d_1)^3\sum_{d_2=1}^{\lfloor\frac{m}{d_1}\rfloor}\mu(d_2)\sum_{x=1}^{\lfloor\frac{n}{d_1d_2}\rfloor}\sum_{y=1}^{\lfloor\frac{m}{d_1d_2}\rfloor}(d_2x)(d_2y)\\ &=\sum_{d_1=1}^{m}(d_1)^3\sum_{d_2=1}^{\lfloor\frac{m}{d_1}\rfloor}\mu(d_2)(d_2)^2\sum_{x=1}^{\lfloor\frac{n}{d_1d_2}\rfloor}\sum_{y=1}^{\lfloor\frac{m}{d_1d_2}\rfloor}xy\\ &=\sum_{d_1=1}^{m}(d_1)^3\sum_{d_2=1}^{\lfloor\frac{m}{d_1}\rfloor}\mu(d_2)(d_2)^2\frac{\lfloor\frac{n}{d_1d_2}\rfloor(\lfloor\frac{n}{d_1d_2}\rfloor+1)}{2} \frac{\lfloor\frac{m}{d_1d_2}\rfloor(\lfloor\frac{m}{d_1d_2}\rfloor+1)}{2}\\ \end{align*} \]

考虑到拆 \((n-x)(m-y)\) 出来的四项不尽相同,外面的系数和内部 \(x,y\) 的次数也不一样,但推出来的式子都是如下的形式:

\[ \sum_{d_1=1}^{m}(d_1)^a\sum_{d_2=1}^{\lfloor\frac{m}{d_1}\rfloor}\mu(d_2)(d_2)^b\sum_{x=1}^{\lfloor\frac{n}{d_1d_2}\rfloor}\sum_{y=1}^{\lfloor\frac{m}{d_1d_2}\rfloor}x^cy^d \]

反正都能做就是了。

直接整除分块套整除分块,外层 \(d_1\) 内层 \(d_2\),同时对 \(n,m\) 两个数整除分块即可,时间复杂度 \(O(n+Tn^{3/4})\)

代码
#include<iostream>
#define int __int128
using namespace std;
const int N = 1e6 + 10;

void read(int &x) {
    char ch;
    while(!isdigit(ch = getchar()));
    x = ch - '0';
    while(isdigit(ch = getchar())) x = x * 10 + ch - '0';
}

void write(int x) {
    if(x == 0) putchar('0');
    char buf[60], top = 0;
    while(x) buf[++top] = x % 10, x /= 10;
    while(top) putchar(buf[top--] + '0');
}

int T, n, m;
int ans1, ans2, ans3;

int s[2][N];

int sum(int n, int a) {
    if(a == 0) return n;
    if(a == 1) return n * (n + 1) / 2;
    if(a == 2) return n * (n + 1) * (2 * n + 1) / 6;
    if(a == 3) return s[0][n];
    return s[1][n];
}

int npri[N], pri[N], pcnt;
int mu[N][3];

int calc(int a, int b, int c, int d) {
    int ans = 0;
    for(int l1 = 1, r1; l1 <= m; l1 = r1 + 1) {
        r1 = min(n / (n / l1), m / (m / l1));
        int n1 = n / l1, m1 = m / l1, res = 0;
        for(int l2 = 1, r2; l2 <= m1; l2 = r2 + 1) {
            r2 = min(n1 / (n1 / l2), m1 / (m1 / l2));
            res += (mu[r2][b] - mu[l2 - 1][b]) * sum(n1 / l2, c) * sum(m1 / l2, d);
        }
        ans += res * (sum(r1, a) - sum(l1 - 1, a));
    }
    return ans;
}

void solve() {
    ans1 = ans2 = ans3 = 0;
    if(n < m) swap(n, m);
    for(int i = 1; i <= n; i++) ans1 += i * (n - i) * m * (m - 1) * n;
    for(int i = 1; i <= m; i++) ans1 += i * (m - i) * n * (n - 1) * m;
    ans2 = n * m * calc(1, 0, 0, 0) - n * calc(2, 1, 0, 1) - m * calc(2, 1, 1, 0) + calc(3, 2, 1, 1);
    ans2 = 2 * n * m * ans2;
    ans3 = n * m * calc(2, 0, 0, 0) - n * calc(3, 1, 0, 1) - m * calc(3, 1, 1, 0) + calc(4, 2, 1, 1);
    ans3 = 4 * ans3;
    write(ans1 + ans2 - ans3);
    putchar('\n');
}

signed main() {

    freopen("grid.in", "r", stdin);
    freopen("grid.out", "w", stdout);

    mu[1][0] = 1;
    for(int i = 2; i < N; i++) {
        if(!npri[i]) { mu[i][0] = -1; pri[++pcnt] = i; }
        for(int j = 1; j <= pcnt; j++) {
            if(i * pri[j] >= N) break;
            npri[i * pri[j]] = 1;
            if(i % pri[j] == 0) {
                mu[i * pri[j]][0] = 0;
                break;
            } else {
                mu[i * pri[j]][0] = -mu[i][0];
            }
        }
    }

    for(int i = 1; i < N; i++) mu[i][1] = mu[i - 1][1] + mu[i][0] * i;
    for(int i = 1; i < N; i++) mu[i][2] = mu[i - 1][2] + mu[i][0] * i * i;
    for(int i = 1; i < N; i++) mu[i][0] = mu[i - 1][0] + mu[i][0];

    for(int i = 1; i < N; i++) s[0][i] = s[0][i - 1] + i * i * i;
    for(int i = 1; i < N; i++) s[1][i] = s[1][i - 1] + i * i * i * i;

    read(T);
    while(T--) {
        read(n), read(m);
        solve();
    }

    return 0;
}