2019ccpc网络赛 9/11

打的太菜了1551,05一眼直接反演了然后化成了不可做的形式哭了,04傻逼了5点整才搞出来,结果差了几s

1001

按位考虑,发现这就是 A & B A \& B A&B,但是我们发现题目要求是正数,那么我们还需要把最低的一边是0一边是1的位改成1才行。

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstdio>
using namespace std;

int main() {
    int t;
    std::cin >> t;
    while(t--) {
        long long a, b, c = 0;
        std::cin >> a >> b;
        c = (a & b);
        if (c == 0) {
            auto t = (a ^ b);
            c = t & -t;
        }
        // c = ((a ^ c) & (b ^ c));
        std::cout << c << '\n';
    }
}

1002

直接权值线段树维护最早出现的位置,每次线段树二分就行(因为是小洛洛写的没想到他居然不会写这个线段树二分,变成了手把手教队友写代码模式

#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
#include <cstdio>
#include <cmath>
#include <cstdio>
#include <cstdint>
#include <utility>
#include <limits>
using namespace std;

// using i64 = int64_t;
using i64 = int;
const int INF = std::numeric_limits<int>::max();

class SegTree {
    int sz, mx;
    SegTree *l, *r;

    void up () {
        mx = std::max(l->mx, r->mx);
    }

    void down () {
        if (!l) {
            auto m = sz / 2;
            l = new SegTree(m);
            r = new SegTree(sz - m);
        }
    }

public:
    SegTree (i64 sz): sz(sz), mx(INF), l(nullptr), r(nullptr) {}
    ~SegTree () { delete l; delete r; }

    void set (i64 pos, i64 val) {
        if (sz == 1) {
            mx = val;
            return;
        }

        down();
        auto m = sz / 2;
        if (pos < m) l->set(pos, val);
        else r->set(pos - m, val);
        up();
    }

    i64 magic (i64 k, i64 x) {
        if (sz == 1)
            return mx > x ? 0 : -1;

        down();
        auto m = sz / 2;
        if (k == 0) {
            if (l->mx > x)
                return l->magic(0, x);
            else if (r->mx > x)
                return m + r->magic(0, x);
            else
                return -1;
        } else if (k < m) {
            auto ret = l->magic(k, x);
            if (ret != -1)
                return ret;

            if (r->mx > x)
                return m + r->magic(0, x);
            return -1;
        } else {
            auto ret = r->magic(k - m, x);
            return ret == -1 ? -1 : m + ret;
        }
    }

};

int main() {
    std::ios::sync_with_stdio(false);

    int t;
    scanf("%d", &t);
    while (t--) {
        int n, m;
        scanf("%d%d", &n, &m);

        auto v = std::vector<int>(n + 1, false);
        auto rt = new SegTree(n + 1);

        for (int i = 1; i <= n; ++i) {
            int c;
            scanf("%d", &c);
            v[i] = c;
            rt->set(c, i);
        }

        i64 ans = 0;
        for (int i = 0; i < m; ++i) {
            int op, a, b;
            scanf("%d%d", &op, &a);

            if (op == 1) {
                a ^= ans;

                // std::cerr << ">1 " << a << ':' << v[a] << '\n';
                if (v[a] != -1) {
                    rt->set(v[a], INF);
                    v[a] = -1;
                }
            } else {
                scanf("%d", &b);
                a ^= ans;
                b ^= ans;

                // std::cerr << ">2 " << a << ' ' << b << '\n';
                ans = rt->magic(b, a);
                if (ans == -1)
                    ans = n + 1;

                printf("%d\n", ans);
            }
        }

        delete rt;
    }
    return 0;
}

1003

最开始yy了一个SAM上倍增找点,主席树第k大的东西,但是一致认为太难写了,后来发现SA的话就相当于从rank[i]开始,往左往右找到第一个height小于它的位置,然后在这个区间中找到第k大就行。(后来发现题解就是那个SAM做法,果然我的SAM水平还是太弱了。

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <cstdlib>

using namespace std;

int const N = 100005;

char s[N];
int n;
int rk[N], sa[N], height[N], w[N], cnt[N], res[N];

void getSa(int up) {
    int *k = rk, *id = height, *r = res;
    for (int i = 0; i < up; ++i) cnt[i] = 0;
    for (int i = 0; i < n; ++i) cnt[k[i] = w[i]]++;
    for (int i = 0; i < up; ++i) cnt[i + 1] += cnt[i];
    for (int i = n - 1; i >= 0; --i) sa[--cnt[k[i]]] = i;
    for (int d = 1; d <= n; d <<= 1) {
        int p = 0;
        for (int i = n - d; i < n; ++i) id[p++] = i;
        for (int i = 0; i < n; ++i) if (sa[i] >= d) id[p++] = sa[i] - d;
        for (int i = 0; i < n; ++i) r[i] = k[id[i]];
        for (int i = 0; i < up; ++i) cnt[i] = 0;
        for (int i = 0; i < n; ++i) cnt[r[i]]++;
        for (int i = 0; i < up; ++i) cnt[i + 1] += cnt[i];
        for (int i = n - 1; i >= 0; --i) sa[--cnt[r[i]]] = id[i];
        swap(k, r);
        p = 0;
        k[sa[0]] = p++;
        for (int i = 0; i < n - 1; ++i) {
            if (sa[i] + d < n && sa[i + 1] + d < n && r[sa[i]] == r[sa[i + 1]] && r[sa[i] + d] == r[sa[i + 1] + d]) {
                k[sa[i + 1]] = p - 1;
            } else {
                k[sa[i + 1]] = p++;
            }
        }
        if (p >= n) return;
        up = p;
    }
}

void getHeight() {
    for (int i = 0; i < n; ++i)
        rk[sa[i]] = i;
    height[0] = 0;
    for (int i = 0, p = 0; i < n; ++i) {
        if (rk[i] == 0) continue;
        int j = sa[rk[i] - 1];
        while (i + p < n && j + p < n && w[i + p] == w[j + p]) ++p;
        height[rk[i]] = p;
        p = max(0, p - 1);
    }
}

void getSuffix(char s[]) {
    n = strlen(s);
    int up = 0;
    for (int i = 0; i < n; ++i) {
        w[i] = s[i];
        up = max(up, w[i]);
    }
    getSa(up + 1);
    getHeight();
}

int f[N][21];

int RMQ(int l, int r) {
    int k = 0;
    while (1 << (k + 1) <= r - l + 1) ++k;
    int x = f[l][k], y = f[r - (1 << k) + 1][k];
    return min(height[x - 1], height[y - 1]);
}

void DP() {
    for (int i = 1; i <= n; ++i) f[i][0] = i;
    for (int j = 1; (1 << j) <= n; ++j)
        for (int i = 1; i + (1 << j) - 1 <= n; ++i) {
            int x = f[i][j - 1], y = f[i + (1 << (j - 1))][j - 1];
            f[i][j] = height[x - 1] < height[y - 1] ? x : y;
        }
}

int getLef(int pos, int val) {
    int ret = 1, l = 1, r = pos;
    while (l <= r) {
        int mid = (l + r) >> 1;
        if (RMQ(mid, pos) < val) {
            ret = mid;
            l = mid + 1;
        } else {
            r = mid - 1;
        }
    }
    return ret;
}

int getRig(int pos, int val) {
    int ret = n + 1, l = pos + 1, r = n;
    while (l <= r) {
        int mid = (l + r) >> 1;
        if (RMQ(pos + 1, mid) < val) {
            ret = mid;
            r = mid - 1;
        } else {
            l = mid + 1;
        }
    }
    return ret;
}

struct node {
    int lc, rc, s;
} t[N * 32];
int root[N], segn;

void insert(int& k, int lk, int l, int r, int pos) {
    k = ++segn;
    t[k] = t[lk];
    t[k].s++;
    if (l == r) return;
    int mid = (l + r) >> 1;
    if (pos <= mid) {
        insert(t[k].lc, t[lk].lc, l, mid, pos);
    } else {
        insert(t[k].rc, t[lk].rc, mid + 1, r, pos);
    }
}

int query(int k1, int k2, int l, int r, int k) {
    if (l == r) return l;
    int mid = (l + r) >> 1;
    int sum = t[t[k1].lc].s - t[t[k2].lc].s;
    if (k <= sum) {
        return query(t[k1].lc, t[k2].lc, l, mid, k);
    } else {
        return query(t[k1].rc, t[k2].rc, mid + 1, r, k - sum);
    }
}

int main() {
    int T;
    scanf("%d", &T);
    while (T--) {
        int Q;
        scanf("%d%d", &n, &Q);
        for (int i = 0; i <= n; ++i) {
            sa[i] = 0;
            rk[i] = 0;
            height[i] = 0;
            root[i] = 0;
        }
        scanf("%s", s);
        getSuffix(s);
        DP();
        segn = 0;
        t[0] = { 0, 0, 0 };
        for (int i = 0; i < n; ++i) {
            insert(root[i + 1], root[i], 1, n, sa[i] + 1);
        }
        while (Q--) {
            int l, r, k;
            scanf("%d%d%d", &l, &r, &k);
            int pos = rk[l - 1];
            int ql = getLef(pos + 1, r - l + 1);
            int qr = getRig(pos + 1, r - l + 1);
            if (t[root[qr - 1]].s - t[root[ql - 1]].s < k) {
                puts("-1");
                continue;
            }
            printf("%d\n", query(root[qr - 1], root[ql - 1], 1, n, k));
        }
    }
}

1004

显然就把所有的边最开始加进堆里,把每个点的出边按权值排序,每次的状态表示为
( l e n , e n d p o i n t , K , l a s t b e g i n p o i n t ) (len, endpoint, K, lastbeginpoint) (len,endpoint,K,lastbeginpoint)
分别表示当前路径的长度,路径的终点,以及最后一条边是从 l a s t b e g i n p o i n t lastbeginpoint lastbeginpoint开始到 e n d p o i n t endpoint endpoint的,而且是 l a s t b e g i n p o i n t lastbeginpoint lastbeginpoint的第K大的边。那么只有两种转移,要么把第 K K K条边变成 l a s t b e g i n p o i n t lastbeginpoint lastbeginpoint的第 K + 1 K+1 K+1条边,要么往后延伸一条边。
但是要注意最开始加入的边是不能有第一种转移的!(草搞了我好久最后在4:58意识到问题5:00改了出来)

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#include <vector>
#include <queue>
#include <functional>

using namespace std;

int const N = 100005;

struct Ed {
    int x, y, w;
} b[N];

struct edge {
    int y, w;
    bool operator < (edge const& oth) const {
        return w < oth.w;
    }
};
vector<edge> G[N];
int n, m, Q;

struct ques {
    int k;
    long long ans;
    int id;
    bool operator < (ques const& oth) const {
        return k < oth.k;
    }
} q[N];

bool cmpid(ques const& x, ques const& y) {
    return x.id < y.id;
}

struct node {
    long long len;
    int y, k, las;
    bool f;
    bool operator < (node const& oth) const {
        return len < oth.len;
    }
    bool operator > (node const& oth) const {
        return len > oth.len;
    }
};

priority_queue<node, vector<node>, greater<node> > que;

int main() {
    int T;
    scanf("%d", &T);
    while (T--) {
        scanf("%d%d%d", &n, &m, &Q);

        while (que.empty() == 0) que.pop();

        for (int i = 1; i <= m; ++i) {
            scanf("%d%d%d", &b[i].x, &b[i].y, &b[i].w);
            G[b[i].x].push_back({ b[i].y, b[i].w });
        }
        for (int i = 1; i <= n; ++i) sort(G[i].begin(), G[i].end());
        for (int i = 1; i <= n; ++i) {
            for (int j = 0; j < (int)G[i].size(); ++j) {
                que.push({ G[i][j].w, G[i][j].y, j, i, 0 });
            }
        }
        for (int i = 1; i <= Q; ++i) {
            scanf("%d", &q[i].k);
            q[i].id = i;
        }
        sort(q + 1, q + Q + 1);
        int p = 1, np = 0;
        while (p <= Q && que.empty() == 0) {
            auto now = que.top();
            que.pop();
            ++np;
            while (p <= Q && q[p].k == np) {
                q[p].ans = now.len;
                ++p;
            }
            if (G[now.y].size() > 0u)
                que.push({ now.len + G[now.y][0].w, G[now.y][0].y, 0, now.y, 1 });
            if (now.f && now.k + 1 < (int)G[now.las].size()) {
                que.push({ now.len + G[now.las][now.k + 1].w - G[now.las][now.k].w, G[now.las][now.k + 1].y, now.k + 1, now.las, 1 });
            }
        }
        sort(q + 1, q + Q + 1, cmpid);
        for (int i = 1; i <= Q; ++i)
            printf("%lld\n", q[i].ans);

        for (int i = 1; i <= n; ++i)
            G[i].clear();
    }
}

1005

以后傻子才直接看到互质就莫反。
update:补了直接莫反的做法。(今早直接去刷了点杜教筛的题发现还是我太垃圾)
首先大力猜一个结论,那就是 g c d ( a n − b n , a m − b m ) = a g c d ( n , m ) − b g c d ( n , m ) gcd(a^n-b^n, a^m-b^m) =a^{gcd(n,m)}-b^{gcd(n,m)} gcd(anbn,ambm)=agcd(n,m)bgcd(n,m)
虽然这个式子很难猜,但是在这里因为 n n n m m m是互质的,猜是 a − b a-b ab还是很容易的
考虑直接拆开式子
∑ i = 1 n ∑ j = 1 i ( i − j ) [ g c d ( i , j ) = = 1 ] \sum_{i=1}^n \sum_{j = 1}^i (i - j) [gcd(i, j)==1] i=1nj=1i(ij)[gcd(i,j)==1]
= ∑ i = 1 n ∑ j = 1 i i [ g c d ( i , j ) = = 1 ] − ∑ i = 1 n ∑ j = 1 i j [ g c d ( i , j ) = = 1 ] =\sum_{i=1}^n \sum_{j = 1}^i i [gcd(i, j)==1] - \sum_{i=1}^n \sum_{j = 1}^i j [gcd(i, j)==1] =i=1nj=1ii[gcd(i,j)==1]i=1nj=1ij[gcd(i,j)==1]
= ∑ i = 1 n i φ ( i ) − ∑ i = 1 n i ∗ φ ( i ) + [ i = = 1 ] 2 =\sum_{i=1}^n i \varphi(i) - \sum_{i=1}^n \frac{i * \varphi(i) + [i == 1]}{2} =i=1niφ(i)i=1n2iφ(i)+[i==1]
= ∑ i = 1 n i ∗ φ ( i ) − 1 2 =\frac{\sum_{i=1}^ni * \varphi(i) - 1}{2} =2i=1niφ(i)1
然后我们给 i ∗ φ ( i ) i*\varphi(i) iφ(i)卷上 i d id id就可以杜教筛了。
直接莫反的话会得到
1 2 ∗ ∑ d = 1 n d ∗ μ [ d ] ∗ ∑ i = 1 [ n d ] i 2 − i \frac{1}{2} * \sum_{d = 1}^n d * \mu[d] * \sum_{i = 1}^{[\frac{n}{d}]} i ^ 2 - i 21d=1ndμ[d]i=1[dn]i2i
同样可以直接数论分块,对于左边的部分卷上 i d id id就可以杜教筛了。代码见第二份

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <algorithm>

using namespace std;

using ll = long long;

int const M = 1000000;
int const N = M + 5;
ll const mod = 1e9 + 7;

ll KSM(ll a, ll k) {
	ll ret = 1;
	for (; k; k >>= 1, a = a * a % mod) 
		if (k & 1)
			ret = ret * a % mod;
	return ret;
}

ll f[N];
int pri[N], tot;
bool isp[N];
ll n, inv2, inv3;

void SAI() {
	isp[1] = 1;
	f[1] = 1;
	for (int i = 2; i <= M; ++i) {
		if (isp[i] == 0) {
			pri[++tot] = i;
			f[i] = i - 1;
		}
		for (int j = 1; j <= tot && i * pri[j] <= M; ++j) {
			isp[i * pri[j]] = 1;
			if (i % pri[j]) 
				f[i * pri[j]] = f[i] * f[pri[j]];
			else {
				f[i * pri[j]] = f[i] * pri[j];
				break;
			}
		}
	}
	for (int i = 1; i <= M; ++i) 
		f[i] = (1ll * i * f[i] % mod + f[i - 1]) % mod;
}

#define G 83333

ll p1[G], p2[G];

ll& gm(ll x) {
	if (x < G) 
		return p1[x];
	else
		return p2[n / x];
}

ll sum_(ll x) {
	if (x <= M) 
		return f[x];
	ll& qs = gm(x);
	if (qs != -1)
		return qs;
	ll ret = x * (x + 1) % mod * inv2 % mod * (2 * x + 1) % mod * inv3 % mod;
	for (ll l = 2, r; l <= x; l = r + 1) {
		r = x / (x / l);
		ret -= (l + r) * (r - l + 1) % mod * inv2 % mod * sum_(x / l) % mod;
		ret %= mod;
	}
	ret = (ret + mod) % mod;
	return qs = ret;
}

ll get_sum(ll x) {
	if (x <= M)
		return f[x];
	memset(p1, -1, sizeof(p1));
	memset(p2, -1, sizeof(p2));
	return sum_(x);
}

int main() {
	SAI();
	int T;
	scanf("%d", &T);
	inv2 = KSM(2, mod - 2);
	inv3 = KSM(3, mod - 2);
	while (T--) {
		ll t1, t2;
		scanf("%lld%lld%lld", &n, &t1, &t2);
		ll ans = (get_sum(n) - 1 + mod) % mod;
		ans = ans * inv2 % mod;
		printf("%lld\n", ans);
	}
}

直接莫反版本

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <unordered_map>

using namespace std;

using ll = long long;

int const M = 1e6;
int const N = M + 5;
int const mod = 1e9 + 7;

ll KSM(ll a, ll k) {
	ll ret = 1;
	for (; k; k >>= 1, a = a * a % mod) 
		if (k & 1)
			ret = ret * a % mod;
	return ret;
}

ll f[N];
int pri[N], tot;
bool isp[N];
ll inv2, inv3;

void SAI() {
	isp[1] = 1;
	f[1] = 1;
	for (int i = 2; i <= M; ++i) {
		if (isp[i] == 0) {
			pri[++tot] = i;
			f[i] = -1;
		}
		for (int j = 1; j <= tot && i * pri[j] <= M; ++j) {
			isp[i * pri[j]] = 1;
			if (i % pri[j]) 
				f[i * pri[j]] = -f[i];
			else {
				f[i * pri[j]] = 0;
				break;
			}
		}
	}
	for (int i = 1; i <= M; ++i) 
		f[i] = (1ll * i * f[i] % mod + f[i - 1]) % mod;
}

unordered_map<int, int> mp;

int sum_(int x) {
	if (x <= M)
		return f[x];
	if (mp.count(x) > 0)
		return mp[x];
	int ret = 1;
	for (int l = 2, r; l <= x; l = r + 1) {
		r = x / (x / l);
		ret = (ret - 1ll * (r - l + 1) * (l + r) % mod * inv2 % mod * sum_(x / l) % mod) % mod;
	}
	ret = (ret + mod) % mod;
	mp[x] = ret;
	return ret;
}

int main() {
	SAI();
	int T;
	scanf("%d", &T);
	inv2 = KSM(2, mod - 2);
	inv3 = KSM(3, mod - 2);
	while (T--) {
		int n, t1, t2;
		scanf("%d%d%d", &n, &t1, &t2);
		int ans = 0;
		for (int l = 1, r; l <= n; l = r + 1) {
			r = n / (n / l);
			int nd = n / l;
			t2 = 1ll * (1 + nd) * nd % mod * inv2 % mod;
			t1 = 1ll * t2 * (2 * nd + 1) % mod * inv3 % mod;
			t1 = t1 - t2;
			t1 = t1 < 0 ? t1 + mod : t1;
			ans = (ans + 1ll * t1 * (sum_(r) - sum_(l - 1))) % mod;
		}
		ans = (1ll * ans * inv2 % mod + mod) % mod;
		printf("%d\n", ans);
	}
}

1006

#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
#include <cstdio>
#include <cmath>
#include <cstdio>
#include <cstdint>
#include <utility>
using namespace std;

using i64 = int64_t;
using Pr = std::pair<int, int>;

int main() {
    std::ios::sync_with_stdio(false);

    int n, m;
    std::cin >> n >> m;
    auto idx = std::vector<Pr>(n);
    for (int i = 0; i < n; ++i) {
        int c;
        std::cin >> c;
        idx[c - 1] = { i, c };
    }

    int mn = 0;
    while(m--) {
        int c;
        std::cin >> c;
        idx[c - 1].first = --mn;
    }

    std::sort(std::begin(idx), std::end(idx));
    for(auto &c: idx)
        std::cout << c.second << ' ';
    // std::cout << '\n';

    return 0;
}

1007

#include <iostream>
#include <vector>
#include <string>
#include <cstdio>
#include <cmath>
#include <cstdio>
#include <cstdint>
using namespace std;

using i64 = int64_t;

using Mat = std::vector<std::string>;

void ccpc(Mat &m, int k, int x, int y, int idx) {
    const char *const CHR = "CP";
    if (k == 0)
        m[x][y] = CHR[idx];
    else {
        int half = 1 << (k - 1);
        ccpc(m, k - 1, x, y, idx);
        ccpc(m, k - 1, x, y + half, idx);
        ccpc(m, k - 1, x + half, y, !idx);
        ccpc(m, k - 1, x + half, y + half, idx);
    }
}

int main() {
    int t;
    std::cin >> t;
    while(t--) {
        int k;
        std::cin >> k;

        auto mat = Mat(1 << k, std::string(1 << k, '.'));
        ccpc(mat, k, 0, 0, 0);
        for (auto &row: mat)
            std::cout << row << '\n';
    }
}

1008

每次把小段拿出来搞就行

#include <iostream>
#include <algorithm>
#include <stdio.h>
#include <queue>
using namespace std;

const int maxn = 100010;
int t[maxn];

priority_queue<int> q;

bool cmp(int a,int b) {return a > b;}

int main() {
    int T;
    scanf("%d",&T);
    while(T--) {
        while(!q.empty()) q.pop();
        int n,k;
        scanf("%d%d",&n,&k);

        for(int i = 0;i < n;i++) {
            scanf("%d",&t[i]);
        }

        sort(t,t+n,cmp);

        long long ans = 0;
        long long fish = 0;
        for(int i = 0;i < n;i++) {
            if(fish == 0) {
                if(q.empty()) ans += k;
                else {
                    ans += k-q.top();
                    q.pop();
                }
                fish++;
            }

            fish += t[i]/k;
            ans += t[i];
            if(t[i]%k != 0) q.push(t[i]%k);

            fish--;
        }

        printf("%lld\n",ans);

    }
    return 0;
}

1009

1010

我们可以设区间内 c i c_i ci i i i这个数出现的次数。
那么就会有
∑ i = l r ∑ j = l r φ ( g c d ( a i , a j ) ) l c m ( a i , a j ) \sum_{i = l}^r \sum_{j = l}^r \varphi(gcd(a_i, a_j))lcm(a_i, a_j) i=lrj=lrφ(gcd(ai,aj))lcm(ai,aj)
= ∑ d = 1 n ∑ i = 1 n ∑ j = 1 n [ g c d ( i , j ) = = d ] c i c j φ ( d ) i j d =\sum_{d = 1}^n \sum_{i = 1}^n \sum_{j = 1}^n [gcd(i, j) == d] c_i c_j \varphi(d) \frac{ij}{d} =d=1ni=1nj=1n[gcd(i,j)==d]cicjφ(d)dij
然后大力反演,就会有
= ∑ d = 1 d φ ( d ) ∑ T = 1 [ n / d ] T 2 μ [ T ] ∑ i = 1 n / d T ∑ j = 1 n / d T c i T d c j T d i j =\sum_{d = 1} d \varphi(d) \sum_{T = 1}^{[n / d]} T ^ 2 \mu[T] \sum_{i =1}^{n / dT} \sum_{j =1}^{n / dT} c_{i T d} c_{j T d} ij =d=1dφ(d)T=1[n/d]T2μ[T]i=1n/dTj=1n/dTciTdcjTdij
然后显然后面那两块是一样的,那么就是
= ∑ d = 1 d φ ( d ) ∑ T = 1 [ n / d ] T 2 μ [ T ] ( ∑ i = 1 n / d T c i T d i ) 2 =\sum_{d = 1} d \varphi(d) \sum_{T = 1}^{[n / d]} T ^ 2 \mu[T] (\sum_{i =1}^{n / dT} c_{i T d} i) ^ 2 =d=1dφ(d)T=1[n/d]T2μ[T](i=1n/dTciTdi)2
然后我们设 j = T d j = Td j=Td
这样就化成了
∑ j = 1 n j ∑ T ∣ j T μ [ T ] φ [ j T ] ( ∑ i = 1 n / d T c i T d i ) 2 \sum_{j = 1}^n j \sum_{T | j} T \mu[T] \varphi[\frac{j}{T}] (\sum_{i =1}^{n / dT} c_{i T d} i) ^ 2 j=1njTjTμ[T]φ[Tj](i=1n/dTciTdi)2
但是中间那里的 ∑ T ∣ j T μ [ T ] φ [ j T ] \sum_{T | j} T \mu[T] \varphi[\frac{j}{T}] TjTμ[T]φ[Tj]实际上就是 f ∗ φ = f ∗ i d ∗ μ = e ∗ μ = μ f * \varphi = f * id * \mu = e * \mu = \mu fφ=fidμ=eμ=μ
所以式子实际上就是
∑ j = 1 n j μ [ j ] ( ∑ i = 1 [ n / j ] i c i j ) 2 \sum_{j = 1}^n j \mu[j] (\sum_{i = 1}^{[n/ j]} i c_{ij})^ 2 j=1njμ[j](i=1[n/j]icij)2
然后考虑莫队维护答案即可,就算算增量就完事了。
不过upd那个函数写好看点才行,不然太容易T了。

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <cassert>
#include <ctime>
#include <vector>
#include <utility>

using namespace std;

int const N = 100005;
int const M = 10000000;

int read() {
	int d = 0;
	char s = getchar();
	while (s < '0' || s > '9') 
		s = getchar();
	while (s >= '0' && s <= '9') {
		d = d * 10 + s - '0';
		s = getchar();
	}
	return d;
}

int mu[M + 5], pri[M / 3], tot;
bool isp[M + 5];

void SAI() {
	isp[1] = 1;
	mu[1] = 1;
	for (int i = 2; i <= M; ++i) {
		if (isp[i] == 0) {
			mu[i] = -1;
			pri[++tot] = i;
		}
		for (int j = 1; j <= tot && i * pri[j] <= M; ++j) {
			isp[i * pri[j]] = 1;
			if (i % pri[j]) 
				mu[i * pri[j]] = -mu[i];
			else {
				mu[i * pri[j]] = 0;
				break;
			}
		}
	}
}

struct ques {
	int l, r, id, posl;
	unsigned int ans;
} q[N];
int bsz;

bool cmpq(ques const& a, ques const& b) {
	if (a.posl == b.posl)
		return a.r < b.r;
	return a.posl < b.posl;
}

bool cmpid(ques const& a, ques const& b) {
	return a.id < b.id;
}

unsigned int a[N];
vector<pair<unsigned int, unsigned int> > p[N];
int n, m;
unsigned int ans;
unsigned int d[M];

inline void upd(int k, unsigned int op) {
	for (auto const& pr : p[k]) {
		unsigned int i = a[k] / pr.first * op;
		ans += pr.second * i * (d[pr.first] * 2u + i);
		d[pr.first] += i;
	}
}

inline void solve() {
	ans = 0;
	upd(1, 1);
	for (int i = 1, L = 1, R = 1; i <= m; ++i) {
		for (; R < q[i].r; ++R) 
			upd(R + 1, 1);
		for (; L > q[i].l; --L)
			upd(L - 1, 1);
		for (; R > q[i].r; --R)
			upd(R, -1);
		for (; L < q[i].l; ++L)
			upd(L, -1);
		q[i].ans = ans;
	}
}

int main() {
	SAI();
	int T = read();
	while (T--) {
		n = read(), m = read();
		for (int i = 1; i <= n; ++i) {
			a[i] = read();
			p[i].clear();
			for (int j = 1; j * j <= a[i]; ++j) 
				if (a[i] % j == 0) {
					if (mu[j] != 0)
						p[i].emplace_back(j, j * mu[j]);
					if (j * j != a[i] && mu[a[i] / j] != 0) 
						p[i].emplace_back(a[i] / j, a[i] / j * mu[a[i] / j]);
					d[j] = d[a[i] / j] = 0;
				}
		}
		bsz = sqrt(m);
		for (int i = 1; i <= m; ++i) {
			q[i].l = read();
			q[i].r = read();
			q[i].posl = (q[i].l - 1) / bsz + 1;
			q[i].id = i;
			q[i].ans = 0;
		}
		sort(q + 1, q + m + 1, cmpq);
		solve();
		sort(q + 1, q + m + 1, cmpid);
		for (int i = 1; i <= m; ++i)
			printf("%u\n", q[i].ans);
	}
}

1011

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值