2019牛客暑期多校训练营(第七场)

Contest Info


[Practice Link](https://ac.nowcoder.com/acm/contest/887#question)
Solved A B C D E F G H I J K
10/11 O O O O O Ø - O Ø O Ø
  • O 在比赛中通过
  • Ø 赛后通过
  • ! 尝试了但是失败了
  • - 没有尝试

Solutions


A. String

题意:
给出一个字符串,将它分段,使得段数最少并且每段字符串的最小表示就是它本身。

思路:
\(n\)很小,考虑贪心枚举长度,如果是最小表示就取这一段,然后将剩下的继续做。

代码:

#include <bits/stdc++.h>
using namespace std;
 
#define N 210
int n;
string s;
vector <string> res;
 
int minRep(string s) {
    int len = s.size();
    int i = 0, j = 1, k = 0;
    while (i < len && j < len && k < len) {
        int t = s[(i + k) % len] - s[(j + k) % len];
        if (!t) ++k;
        else {
            if (t > 0) i += k + 1;
            else if (t < 0) j += k + 1;
            if (i == j) j = i + 1;
            k = 0;
        }
    //  printf("%d %d %d\n", i, j, k);
    }
    return min(i, j);
}
 
int ok(string s) {
    string t = s;
    s += t;
    if (minRep(s) == 0) return 1;
    return 0;
}
 
void solve(string s) {
    int len = s.size();
    string t = "";
    for (int i = len; i >= 1; --i) {
        if (ok(s)) {
            res.push_back(s);
            reverse(t.begin(), t.end());
            solve(t);
            return;
        } else {
            t += s[i - 1];
            s.pop_back();
        }
    }
}
 
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    int T; cin >> T;
    while (T--) {
        cin >> s;
        res.clear();
        solve(s);
        for (int i = 0, sze = (int)res.size(); i < sze; ++i) {
            cout << res[i] << " \n"[i == sze - 1];
        }
    }
    return 0;
}

B. Irreducible Polynomial

题意:
给出一个多项式,判断其在实数域是不是可约的。

思路:
在实数域不可约的只有一次多项式或者二次多项式,判断一下即可。

代码:

#include <bits/stdc++.h>
 
using namespace std;
 
typedef long long ll;
 
#define N 110
 
int n;
ll a[N];
 
int main() {
#ifdef LOCAL_JUDGE
    freopen("input.txt", "r", stdin);
#endif
    int T;
    scanf("%d", &T);
    while (T--) {
        scanf("%d", &n);
        for (int i = n; i >= 0; --i) {
            scanf("%lld", a + i);
        }
        if (n >= 3) {
            puts("No");
            continue;
        } else if (n <= 1) {
            puts("Yes");
            continue;
        } else if (n == 2) {
            ll A = a[2], B = a[1], C = a[0];
            ll d = B * B - 4 * A * C;
            if (d >= 0) {
                puts("No");
            } else {
                puts("Yes");
            }
        }
    }
    return 0;
}

C. Governing sand

题意:
\(n\)种树,每种树的高度是\(H_i\),砍掉的代价为\(C_i\),个数有\(P_i\)个。
现在要求最高的树的个数要占一半以上,问砍掉一些树后满足要求的最小花费是多少?

思路:
考虑从高到低枚举树的高度,然后将费用作为权值建立权值线段树,直接在线段树上二分即可。

代码:

#include <bits/stdc++.h>
using namespace std;
 
#define ll long long
#define N 100010
const ll INF = 0x3f3f3f3f3f3f3f3f;
int n; ll tot;
struct Hash {
    int a[N], cnt;
    void init() { cnt = 0; }
    void add(int x) { a[++cnt] = x; }
    void work() {
        sort(a + 1, a + 1 + cnt);
        cnt = unique(a + 1, a + 1 + cnt) - a - 1;
    }
    int get(int x) {
        return lower_bound(a + 1, a + 1 + cnt, x) - a;
    }
}hs;
struct node {
    int h, c, p;
    void scan() {
        scanf("%d%d%d", &h, &c, &p);
        hs.add(h);     
        tot += p;
    }
}a[N];
vector <vector<node>> vec;
 
struct SEG {
    struct node {
        ll sum, num, base;
        node() {
            sum = num = base = 0;
        }
        void add(ll x) {
            num += x;
            sum += base * x;
        }
        node operator + (const node &other) const {
            node res = node();
            res.sum = sum + other.sum;
            res.num = num + other.num;
            return res;
        }
    }t[10010];
    void build(int id, int l, int r) {
        t[id] = node();
        if (l == r) {
            t[id].base = l;
            return;
        }
        int mid = (l + r) >> 1;
        build(id << 1, l, mid);
        build(id << 1 | 1, mid + 1, r);
    }  
    void update(int id, int l, int r, int pos, ll x) {
        if (l == r) {
            t[id].add(x);
            return;
        }
        int mid = (l + r) >> 1;
        if (pos <= mid) update(id << 1, l, mid, pos, x);
        else update(id << 1 | 1, mid + 1, r, pos, x);
        t[id] = t[id << 1] + t[id << 1 | 1];
    }
    ll query(int id, int l, int r, ll k) {
        if (k <= 0) return 0;
        if (l == r) {
            return k * t[id].base;
        }
        int mid = (l + r) >> 1;
        if (t[id << 1].num >= k) {
            return query(id << 1, l, mid, k);
        } else {
            return t[id << 1].sum + query(id << 1 | 1, mid + 1, r, k - t[id << 1].num);
        }
    }
}seg;
 
int main() {
    while (scanf("%d", &n) != EOF) {
        tot = 0;
        hs.init();
        for (int i = 1; i <= n; ++i) a[i].scan();
        hs.work();
        vec.clear(); vec.resize(n + 1);
        for (int i = 1; i <= n; ++i) {
            vec[hs.get(a[i].h)].push_back(a[i]);
        }
        int m = 200;
        seg.build(1, 1, m);
        ll fee = 0, res = INF;
        for (int i = 1; i <= n; ++i) {
            seg.update(1, 1, m, a[i].c, a[i].p);
        }
        for (int i = hs.cnt; i >= 1; --i) {
            ll tmp = 0, now = 0;
            for (auto it : vec[i]) {
                now += it.p;
                tmp += 1ll * it.c * it.p;
                seg.update(1, 1, m, it.c, -it.p);  
            }
            tot -= now;
            res = min(res, fee + seg.query(1, 1, m, tot - now + 1));
            fee += tmp;
        }  
        printf("%lld\n", res);
    }
    return 0;
}

D.Number

签到。

E. Find the median

题意:
每次增加\([L_i, R_i]\)之间的所有数到序列中,然后询问序列的中位数。

思路:
考虑每次加入的是一条线段,我们考虑二分的时候考虑线段的左端点在其左边以及右端点在其左边的。
我们假设一条线段是\([L, R]\),并且其左端点都在\(x\)的左边,那么我们考虑这么算贡献:

\[\begin{eqnarray*} R - L + 1 = x - (L - 1) - (x - R) \end{eqnarray*} \]

这样对于每个左端点以及右端点它们的贡献和\(x\)无关,只需要关心其个数即可。
线段树维护一下相关信息,然后二分即可。
注意\(L, R\)很大,离散化之后取值可能在左区间和右区间的中间,如果是的话,直接在这段范围内二分即可。

代码:

#include <bits/stdc++.h>
using namespace std;
 
#define ll long long
#define N 400010
struct Hash {
    int a[N << 1], cnt;
    void init() { cnt = 0; }
    void add(int x) { a[++cnt] = x; }
    void work() {
        sort(a + 1, a + 1 + cnt);
        cnt = unique(a + 1, a + 1 + cnt) - a - 1;
    }
    int get(int x) { return lower_bound(a + 1, a + 1 + cnt, x) - a; }
}hs;
int n, m;
ll A[2], B[2], C[2], M[2];
ll L[N], R[N], X[N], Y[N];
 
struct SEG {
    struct node {
        int base;
        //0表示左端点 1表示右端点
        int num[2];
        ll sum[2];
        node() {
            num[0] = num[1] = 0;
            sum[0] = sum[1] = 0;
        }
        void add(ll x, int f) {
            num[f] += x;
            sum[f] += 1ll * x * base;
        }
        node operator + (const node &other) const {
            node res = node();
            for (int i = 0; i < 2; ++i) {
                res.num[i] = num[i] + other.num[i];
                res.sum[i] = sum[i] + other.sum[i];
            }
            return res;
        }
    }t[N << 3], base;
    void build(int id, int l, int r) {
        t[id] = node();
        if (l == r) {
            t[id].base = hs.a[l] - 1;
            return;
        }
        int mid = (l + r) >> 1;
        build(id << 1, l, mid);
        build(id << 1 | 1, mid + 1, r);
    }
    void update(int id, int l, int r, int pos, ll x, int f) {
        if (l == r) {
            t[id].add(x, f);
            return;
        }
        int mid = (l + r) >> 1;
        if (pos <= mid) update(id << 1, l, mid, pos, x, f);
        else update(id << 1 | 1, mid + 1, r, pos, x, f);
        t[id] = t[id << 1] + t[id << 1 | 1];
    }
    int query(int id, int l, int r, ll k, node left) {
//      printf("## %d %d %lld\n", l, r, k)  ;
        if (l == r) return hs.a[l]; 
        node tmp = left + t[id << 1];
        int mid = (l + r) >> 1;
        ll pl = hs.a[mid];
        ll pr = hs.a[mid + 1] - 1;
        ll resl = 1ll * pl * tmp.num[0] - tmp.sum[0] - (1ll * pl * tmp.num[1] - tmp.sum[1] - tmp.num[1]);
        ll resr = 1ll * pr * tmp.num[0] - tmp.sum[0] - (1ll * pr * tmp.num[1] - tmp.sum[1] - tmp.num[1]);
        if (resl >= k) {
            return query(id << 1, l, mid, k, left);
        } else if (resr < k) {
            return query(id << 1 | 1, mid + 1, r, k, tmp);
        } else {
            int ql = pl, qr = pr, res = pr;
            while (qr - ql >= 0) {
                int mid = (ql + qr) >> 1;
                ll tot = 1ll * mid * tmp.num[0] - tmp.sum[0] - (1ll * mid * tmp.num[1] - tmp.sum[1] - tmp.num[1]);
                if (tot >= k) {
                    res = mid;
                    qr = mid - 1;
                } else {
                    ql = mid + 1;
                }
            }
            return res;
        }
    }
}seg; 
 
int main() {
    while (scanf("%d", &n) != EOF) {
        scanf("%lld%lld%lld%lld%lld%lld", X + 1, X + 2, A, B, C, M);
        scanf("%lld%lld%lld%lld%lld%lld", Y + 1, Y + 2, A + 1, B + 1, C + 1, M + 1);
        hs.init();
        L[1] = X[1] + 1; R[1] = Y[1] + 1; if (L[1] > R[1]) swap(L[1], R[1]); hs.add(L[1]); hs.add(R[1]);
        L[2] = X[2] + 1; R[2] = Y[2] + 1; if (L[2] > R[2]) swap(L[2], R[2]); hs.add(L[2]); hs.add(R[2]);
        for (int i = 3; i <= n; ++i) {
            X[i] = (A[0] * X[i - 1] % M[0] + B[0] * X[i - 2] % M[0] + C[0] + M[0]) % M[0];
            Y[i] = (A[1] * Y[i - 1] % M[1] + B[1] * Y[i - 2] % M[1] + C[1] + M[1]) % M[1];
            L[i] = X[i] + 1;
            R[i] = Y[i] + 1;
            if (L[i] > R[i]) swap(L[i], R[i]);
            hs.add(L[i]); hs.add(R[i]);    
        }
    //  for (int i = 1; i <= n; ++i) printf("%lld %lld\n", L[i], R[i]); 
        hs.work();
        m = hs.cnt;
        seg.build(1, 1, m);
        ll tot = 0;
        for (int i = 1; i <= n; ++i) {
            seg.update(1, 1, m, hs.get(L[i]), 1, 0);
            seg.update(1, 1, m, hs.get(R[i]), 1, 1);
            tot += R[i] - L[i] + 1;
            seg.base = SEG::node();
            printf("%d\n", seg.query(1, 1, m, (tot + 1) / 2, seg.base));
        }
    }
    return 0;
}

E. Find the median

题意:
\(n\)块能量石,每秒钟会增加\(L_i\)的能量,但是一旦增长到了\(C_i\)它就不会增长了,它初始的能量为\(E_i\)
现在有若干个时刻\(t_i\),会选择下标在\([S_i, T_i]\)的能量石吸取它们的能量,这些能量石的能量变为\(0\),并依据上述规则继续增长。
问最后一共吸取了多少能量?

思路:
考虑对每块石头吸取了多少能量。
假如我们能够维护出每块石头被吸取能量的若干个时间点,那么它们的增长能量的时间区间会是一条条线段。
那么考虑这些线段长度大于等于\(\left\lceil \frac{C_i}{L_i} \right\rceil\)的线段,它们的贡献是\(C_i\),其他线段都没有长满,那么是\(t_i \cdot L_i\)
这个线段的长度可以用权值线段树维护。
那么考虑如何维护线段?
用一个\(set\),考虑插入一个时间点会让一条原本的线段分裂成两段,删除一个时间点会让两个线段合并。
那么对于一次吸取操作\((t_i, S_i, T_i)\),相当于我们枚举到\(S_i\)的时候插入\(t_i\)这个时间点,枚举到\(T_i + 1\)的时候删除\(t_i\)这个时间点。

代码:

#include <bits/stdc++.h>
using namespace std;
 
#define ll long long
#define N 200010
int n, q;
struct node {
    int e, l, c;
    void scan() {
        scanf("%d%d%d", &e, &l, &c);
    }
}a[N];
vector <vector<int>> add, del;
 
struct SEG {
    struct node {
        ll sum, num;
        int base;
        node() {
            sum = num = 0;
        }
        void add(ll x) {
            sum += x * base;
            num += x;
        }
        node operator + (const node &other) const {
            node res = node();
            res.sum = sum + other.sum;
            res.num = num + other.num;
            return res;
        }
    }t[N << 2], res;
    void build(int id, int l, int r) {
        t[id] = node();
        if (l == r) {
            t[id].base = l;
            return;
        }
        int mid = (l + r) >> 1;
        build(id << 1, l, mid);
        build(id << 1 | 1, mid + 1, r);
    }
    void update(int id, int l, int r, int pos, int x) {
        if (pos == 0) return;
        if (l == r) {
            t[id].add(x);
            return;
        }
        int mid = (l + r) >> 1;
        if (pos <= mid) update(id << 1, l, mid, pos, x);
        else update(id << 1 | 1, mid + 1, r, pos, x);
        t[id] = t[id << 1] + t[id << 1 | 1];
    }
    void query(int id, int l, int r, int ql, int qr) {
        if (ql > qr) return;
        if (l >= ql && r <= qr) {
            res = res + t[id];
            return;
        }
        int mid = (l + r) >> 1;
        if (ql <= mid) query(id << 1, l, mid, ql, qr);
        if (qr > mid) query(id << 1 | 1, mid + 1, r, ql, qr);
    }
}seg;
 
int main() {
    int T; scanf("%d", &T);
    for (int kase = 1; kase <= T; ++kase) {
        printf("Case #%d: ", kase);
        scanf("%d", &n);
        add.clear(); del.clear();
        add.resize(n + 10); del.resize(n + 10);
        for (int i = 1; i <= n; ++i) a[i].scan();
        scanf("%d", &q);
        for (int i = 1, S, T, t; i <= q; ++i) {
            scanf("%d%d%d", &t, &S, &T);
            add[S].push_back(t);
            del[T + 1].push_back(t);
        }      
        int m = 200000;
        ll res = 0;
        seg.build(1, 1, m);
        set <int> se;
        int Fi = 0;
        for (int i = 1; i <= n; ++i) {
            for (auto it : add[i]) {
                if (se.empty()) {
                    se.insert(it);
                    Fi = it; 
                } else {
                    auto pos = se.lower_bound(it);
                    if (pos == se.begin()) {
                        Fi = it;
                        seg.update(1, 1, m, *pos - it, 1);
                        se.insert(it);
                    } else if (pos == se.end()) {
                        --pos;
                        seg.update(1, 1, m, it - *pos, 1);
                        se.insert(it);
                    } else {
                        auto pos2 = pos;
                        --pos2;
                        seg.update(1, 1, m, it - *pos2, 1);
                        seg.update(1, 1, m, *pos - it, 1);
                        seg.update(1, 1, m, *pos - *pos2, -1);
                        se.insert(it);
                    }
                }
            }
            for (auto it : del[i]) {
                auto pos = se.lower_bound(it);
                auto nx = pos; ++nx;
                if (pos == se.begin()) {
                    if (nx == se.end()) {
                        Fi = 0;
                    } else {
                        Fi = *nx;
                        seg.update(1, 1, m, *nx - *pos, -1);
                    }
                    se.erase(pos);
                } else if (nx == se.end()) {
                    auto pre = pos; --pre;
                    seg.update(1, 1, m, *pos - *pre, -1);
                    se.erase(pos);
                } else {
                    auto pre = pos; --pre;
                    seg.update(1, 1, m, *pos - *pre, -1);
                    seg.update(1, 1, m, *nx - *pos, -1);
                    seg.update(1, 1, m, *nx - *pre, 1);
                    se.erase(pos);
                }
            }
            if (Fi == 0) continue;
            res += min(1ll * a[i].e + 1ll * a[i].l * Fi, 1ll * a[i].c);
            if (a[i].l == 0) continue;
            int t = ((a[i].c + a[i].l - 1) / a[i].l);   
            seg.res = SEG::node();
            //大于等于t的
            seg.query(1, 1, m, t, m);
            res += 1ll * a[i].c * seg.res.num;
            //小于t的
            seg.res = SEG::node();
            seg.query(1, 1, m, 1, t - 1);
            res += 1ll * seg.res.sum * a[i].l;
        }
        printf("%lld\n", res);
    }
    return 0;
}

H. Pair

题意:
给出三个数\((A, B, C)\),问有多少对\((x, y)\)满足:

  • \(1 \leq x \leq A\)
  • \(1 \leq y \leq B\)
  • \(x\;and\;y > C\) 或者 \(x \; xor \; y < C\)

思路:
考虑\(f[i][j][k][l][m]\)表示:

  • 枚举到前\(i\)
  • \(x\)是否卡到\(A\)的上界
  • \(y\)是否卡到\(B\)的上界
  • \(x \; and \; y\)是否已经大于\(C\)
  • \(x \; and \; y\)是否已经小于\(C\)

最后注意一下\(0\)的贡献,强行删去或者再加两位状态标记。

代码:

#include <bits/stdc++.h>
 
using namespace std;
 
typedef long long ll;
 
#define N 100
 
ll A, B, C;
int a[N], b[N], c[N];
ll dp[N][3][3][2][2][2][2];
 
//位置 x&y x^y (=0 <1 >2)
ll DFS(int pos, int flag1, int flag2, int limit1, int limit2, int one1, int one2) {
    if (pos == -1 && (flag1 == 2 || flag2 == 1) && one1 == 1 && one2 == 1) {
        return 1ll;
    } else if (pos == -1) {
        return 0ll;
    }
    ll &res = dp[pos][flag1][flag2][limit1][limit2][one1][one2];
    if (res != -1) {
        return res;
    }
    res = 0;
    if (flag1 == 1 && flag2 == 2) {//x&y<C x^y>C 不合法
        return 0ll;
    }
    int l1 = limit1 ? a[pos] : 1;
    int l2 = limit2 ? b[pos] : 1;
    for (int i = 0; i <= l1; ++i) {
        for (int j = 0; j <= l2; ++j) {
            int x = i & j;
            int y = i ^j;
            int f1 = flag1;
            if (f1 == 0) {
                if (x > c[pos]) {
                    f1 = 2;
                } else if (x == c[pos]) {
                    f1 = 0;
                } else {
                    f1 = 1;
                }
            }
            int f2 = flag2;
            if (f2 == 0) {
                if (y > c[pos]) {
                    f2 = 2;
                } else if (y == c[pos]) {
                    f2 = 0;
                } else {
                    f2 = 1;
                }
            }
            res += DFS(pos - 1, f1, f2, limit1 && (i == a[pos]),
                       limit2 && (j == b[pos]), one1 | i, one2 | j);
        }
    }
    return res;
}
 
int main() {
#ifdef LOCAL_JUDGE
    freopen("input.txt", "r", stdin);
#endif
    int T;
    scanf("%d", &T);
    while (T--) {
        scanf("%lld %lld %lld", &A, &B, &C);
        for (int i = 30; i >= 0; --i) {
            if (A & (1ll << i)) {
                a[i] = 1;
            } else {
                a[i] = 0;
            }
            if (B & (1ll << i)) {
                b[i] = 1;
            } else {
                b[i] = 0;
            }
            if (C & (1ll << i)) {
                c[i] = 1;
            } else {
                c[i] = 0;
            }
        }
        memset(dp, -1, sizeof dp);
        printf("%lld\n", DFS(30, 0, 0, 1, 1, 0, 0));
    }
    return 0;
}

I. Chessboard

题意:
给出一个\(N\)\(M\),问能构造多少个\(k \cdot k\)的棋盘,使得每一个格子的数字不少于\(M\),并且任取\(K\)个,它们之中没有任意两个来自同一行或者同一列,任取\(k\)个的值的和都相同。

思路:
考虑固定\(k\),那么每个格子至少要放\(m\),那么我们令\(T = n - k \cdot m\)
那么把行列分开,每次想加必定是给一行全都加上或者一列全都加上,
相当于有\(2n\)个桶子,将\(T\)分到这些桶子里,可以有空桶。
那么贡献就是\({T + 2n - 1 \choose 2n - 1}\)
但是考虑到对于每个行的桶子它都分到了大于等于\(1\)的贡献,那么将其都减\(1\),然后将每一列的桶子都加\(1\),那么会得到完全一样的棋盘
那么最终的贡献就是\({T + 2n - 1 \choose 2n - 1} - {T + n - 1 \choose 2n - 1}\)

代码:

#include <bits/stdc++.h>
using namespace std;
 
#define N 6010
#define ll long long
const ll p = 998244353;
int n, m;
ll fac[N], inv[N];
ll qmod(ll base, ll n) {
    ll res = 1;
    while (n) {
        if (n & 1) res = res * base % p;
        base = base * base % p;
        n >>= 1;
    }
    return res;
}
ll C(int n, int m) {
    if (m > n) return 0;
    return fac[n] * inv[m] % p * inv[n - m] % p;
}
void add(ll &x, ll y) {
    x += y;
    if (x >= p) x -= p;
}
 
int main() {
    fac[0] = 1;
    for (int i = 1; i < N; ++i) fac[i] = fac[i - 1] * i % p;
    inv[N - 1] = qmod(fac[N - 1], p - 2);
    for (int i = N - 1; i >= 1; --i) inv[i - 1] = inv[i] * i % p;
    int T; scanf("%d", &T);
    while (T--) {
        scanf("%d%d", &n, &m);
        if (m > n) puts("0");
        else {
            ll res = 0;
            for (int i = 1; i <= n; ++i) {
                for (int j = 0; j <= n - 1ll * i * m; ++j) {
                    add(res, (C(j + 2 * i - 1, 2 * i - 1) - C(j + i - 1, 2 * i - 1) + p) % p);
                }
            }
            printf("%lld\n", res);
        }
    }
    return 0;
}

J. A+B problem

签到。

K. Function

题意:
计算如下积性函数的前缀和:

\[\begin{eqnarray*} f(x) = \left\{ \begin{array}{cccc} 3e + 1 && x = p^e(e \geq 1) \; and \; p \equiv 1 \bmod 4 \\ 1 && else \end{array} \right. \end{eqnarray*} \]

思路:
考虑用\(dp\)方法求出在根号处质数的贡献,然后直接Min25筛即可。

代码:

#include <bits/stdc++.h>
using namespace std;
 
#define N 101000
#define ll long long
ll n, w[N << 1];
int blk;
int pri[N], tot, check[N];
ll sum[N], h1[N << 1], h3[N << 1], f1[N], f3[N]; 
int id1[N], id2[N], m;
void add(ll &x, ll y) {
    x += y;
}
 
ll f(int p, int e) {
    if (p % 4 == 1) return 3 * e + 1;
    else return 1;
}
 
void init(int n) {
    memset(check, 0, sizeof check);
    tot = 0;
    for (int i = 2; i <= n; ++i) {
        if (!check[i]) {
            pri[++tot] = i;
            f1[tot] = f1[tot - 1];
            f3[tot] = f3[tot - 1];
            if (i % 4 == 1) ++f1[tot];
            if (i % 4 == 3) ++f3[tot];
        }
        for (int j = 1; j <= tot; ++j) {   
            if (1ll * i * pri[j] > n) {    
                break;
            }
            check[i * pri[j]] = 1; 
            if (i % pri[j] == 0) {
                break;
            }
        }
    }
}
 
ll S(ll x, int y) {
    if (x <= 1 || pri[y] > x) {
        return 0;
    }
    int k = (x <= blk) ? id1[x] : id2[n / x];
    ll ret = 4ll * h1[k] + h3[k] - 4 * f1[y - 1] - f3[y - 1];
    if (y == 1) ++ret;
    for (int i = y; i <= tot && 1ll * pri[i] * pri[i] <= x; ++i) {
        ll t1 = pri[i], t2 = 1ll * pri[i] * pri[i];
        for (int e = 1; t2 <= x; ++e, t1 = t2, t2 *= pri[i]) {
            add(ret, 1ll * f(pri[i], e) * S(x / t1, i + 1) + f(pri[i], e + 1));
        }
    }
    return ret;
}
 
int main() {
    int T; scanf("%d", &T);
    while (T--) {
        scanf("%lld", &n);
        m = 0;
        init(blk = sqrt(n));
        for (ll i = 1, j; i <= n; i = j + 1) {
            j = n / (n / i);
            w[++m] = n / i;
            h1[m] = w[m] / 4 + (w[m] % 4 >= 1) - 1;
            h3[m] = w[m] / 4 + (w[m] % 4 >= 3);
            if (w[m] <= blk) {
                id1[w[m]] = m;
            } else {
                id2[n / w[m]] = m;  
            }
        }
        for (int j = 1; j <= tot; ++j) {
            for (int i = 1; i <= m && 1ll * pri[j] * pri[j] <= w[i]; ++i) {
                int k = (w[i] / pri[j] <= blk) ? id1[w[i] / pri[j]] : id2[n / (w[i] / pri[j])];
                if (pri[j] % 4 == 1) {
                    add(h1[i], -(h1[k] - f1[j - 1]));
                    add(h3[i], -(h3[k] - f3[j - 1]));
                } else if (pri[j] % 4 == 3) {
                    add(h1[i], -(h3[k] - f3[j - 1]));
                    add(h3[i], -(h1[k] - f1[j - 1]));
                }
            }
        }
        printf("%lld\n", S(n, 1) + 1);
    }
    return 0;
}
posted @ 2019-08-10 08:14  Dup4  阅读(366)  评论(0编辑  收藏  举报