第四屆“傳智杯”全國大學生IT技能大賽 民間題解


目錄

更好的閱讀體驗?

比賽傳送

因為本人參加的是洛谷的同步賽,可能題目順序與正式賽選手不同?

今年題目難度普遍偏低。只有 D,F 還好。

有些題就不粘代碼了。

A

按題目給的公式計算即可。注意應在最后的答案中去掉小數部分。

B

按照題意模擬即可。注意答案要與 \(0\)\(\max\)

C

按照題意模擬即可。注意應該先做乘法在做除法,或者把后面的值用 doublefloat 數據類型的變量存儲,並在最后去掉小數部分。

時間復雜度 \(\mathcal O(n)\)

D

算法一

\(x \oplus y = p\) ,則 \(x \oplus p = y\)。因此我們要找有多少 \(p\) 滿足 \(x \oplus p < x\)

考慮到 \(x \oplus y\) 的值最大為 \(2^{21} - 1\) ,因此我們用線性篩篩出 \(\le 2.1 \times 10^6\) 的所有素數,然后把它們插入一個 01Trie

然后我們要求的答案在 01Trie 中其實是一個個字數內的數的個數的和。

在遍歷 \(x\) 的每一個二進制位的過程中,設該二進制位為 \(c\),當前節點編號為 \(u\)\(tr_{u,0/1}\) 表示下一個分別是 \(0,1\) 的位置的編號。

如果 \(c=1\),那個 \(tr_{u,0}\) 內的所有子樹一定合法,我們加上它子樹內的數的個數,然后讓 \(u \to rt_{u,1}\),即進入右子樹。

如果 \(c=0\),那個子樹都不能造成貢獻,我們直接進入 \(tr_{u,0}\),即左子樹。

如果某個非起始位置 \(u=0\) ,直接退出即可。

時間復雜度為 \(\mathcal O((Cnt + m) \log V)\),其中 \(Cnt\) 是質數個數,\(V\) 是值域。

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2.1e6 + 10;

int T, x;
int ans[MAXN];
int prim[MAXN], Cnt = 0;
bool vis[MAXN], flag[MAXN];

void Init() {
    int M = 2100000;
    for(int i = 2; i <= M; ++i) {
        if(!vis[i]) prim[++Cnt] = i;
        for(int j = 1; j <= Cnt && i * prim[j] <= M; ++j) {
            vis[i * prim[j]] = true;
            if(i % prim[j] == 0) break;
        }
    }
}

namespace Trie {
    int tr[MAXN << 2][2], node_num = 0, siz[MAXN << 2];
    void Insert(int x) {
        int u = 0;
        siz[0] ++;
        for(int j = 21; j >= 0; --j) {
            int c = (x >> j) & 1;
            if(!tr[u][c]) tr[u][c] = ++node_num;
            u = tr[u][c];
            siz[u] ++;
        }
    }
    int Query(int x) {
        int u = 0, res = 0;
        for(int j = 21; j >= 0; --j) {
            int c = (x >> j) & 1;
            if(c) {
                res = res + siz[tr[u][c]];
                u = tr[u][c ^ 1];
            } else {
                u = tr[u][c];
            }
            if(!u) return res;
        }
        return res;
    }
}

int main() {
    Init();
    for(int i = 1; i <= Cnt; ++i) {
        Trie::Insert(prim[i]);
    }
    scanf("%d", &T);
    while(T--) {
        scanf("%d", &x);
        int res = Trie::Query(x);
        printf("%d\n", res);
    }
    return 0;
}

算法二

考慮上面那個問題的本質。

其實問題可以再次轉化成有多少 \(p\) 滿足:如果設 \(x\)\(p\) 第一個不同的位置 \(i\)\(x\)\(i\) 這一位上為 \(1\)

進一步分析可以發現,如果 \(x\) 的第 \(i\) 位為 \(1\),那么所有 \(2^{i} \le p < 2^{i+1}\) 的質數都是合法的。

這其實和 \(p\) 的最高位數有關,按照這個預處理個數即可。

總復雜度 \(\mathcal O((Cnt + m) \log V)\)

#include<bits/stdc++.h> // 這個代碼雖然沒交但應該沒有問題
using namespace std;
const int MAXN = 2.1e6 + 10;

int T, x;
int ans[MAXN], a[200];
int prim[MAXN], Cnt = 0;
bool vis[MAXN], flag[MAXN];

int read() {
    int s = 0, f = 0;
    char ch = getchar();
    while(!isdigit(ch)) f |= (ch == '-'), ch = getchar();
    while(isdigit(ch)) s = (s << 1) + (s << 3) + (ch ^ 48), ch = getchar();
    return f ? -s : s;
}

void Init() {
    int M = 2100000;
    for(int i = 2; i <= M; ++i) {
        if(!vis[i]) prim[++Cnt] = i;
        for(int j = 1; j <= Cnt && i * prim[j] <= M; ++j) {
            vis[i * prim[j]] = true;
            if(i % prim[j] == 0) break;
        }
    }
}

int main() {
    Init();
    for(int i = 1; i <= Cnt; ++i) {
        int x = prim[i];
        int lst, cnt = 0;
        while(x) {
            if(x & 1) lst = cnt;
            x >>= 1, ++ cnt;
        }
        a[lst] ++;
    }
    cin >> T;
    while(T--) {
        int Ans = 0;
        cin >> x;
        for(int i = 0; i <= 21; ++i) 
            if(x & (1 << i)) Ans += a[i];
        printf("%d\n", Ans);
    }
    return 0;
}

E

題意可以轉化成有 \(k\) 個可重集,每次向里面插入 \(p\) 個數對 \((a,b)\) ,每個數對 \(a,b\) 表示向第 \(a\) 個集合中插入一個值為 \(b\) 的數。

查詢的話就是第 \(x\) 個集合中在 \([y_{\min},y_{\max}]\) 內的數有多少個。

發現 \(k,n\) 都很小,對於每個集合維護一個樹狀數組即可。

時間復雜度 \(\mathcal O(n \log n)\)

#include<bits/stdc++.h>
using namespace std;

int n, k, M = 1000;

struct Bit {
    int sum[1010];
    int lb(int x) { return x & -x; }
    void Modify(int x, int k) { for(; x <= M; x += lb(x)) sum[x] += k; }
    int Query(int x) { int res = 0; for(; x; x -= lb(x)) res += sum[x]; return res; }
}bit[1010];


int main() {
    cin >> n >> k;
    for(int i = 1, opt, p; i <= n; ++i) {
        cin >> opt >> p;
        if(opt == 1) {
            for(int j = 1, x, y; j <= p; ++j) {
                cin >> x >> y;
                bit[x].Modify(y, 1);
            }
        } else {
            int amax, amin;
            cin >> amin >> amax;
            int res = bit[p].Query(amax) - bit[p].Query(amin - 1);
            cout << res << "\n";
        }
    }
}

F

考慮利用 \(dfs\) 序轉化到序列上。

考慮把深度當成主席樹的值域維護,每個位置對應深度權值為 \(1\)

操作一可以直接用一個 lst 變量記錄。

操作二就可以直接查詢 \([dfn_x, dfn_x + siz_x - 1]\) 這段區間中 \(dep_{pre_i} \ge lst\) 的位置有多少。

注意特判一下一開始整個樹都是綠的。

時間復雜度 \(\mathcal O(n \log n)\)

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1e5 + 10;

struct edge {
    int to, nxt;
}e[MAXN << 1];
int head[MAXN], num_edge = 1;

int n, m;
int root[MAXN];
int siz[MAXN], dfn[MAXN], pre[MAXN], dep[MAXN], Cnt = 0;

namespace Hjt {
    #define ls lson[now_]
    #define rs rson[now_]
    int node_num = 0, siz[MAXN << 5], lson[MAXN << 5], rson[MAXN << 5];
    void Insert(int &now_, int pre_, int l, int r, int pos, int k) {
        now_ = ++node_num;
        siz[now_] = siz[pre_] + k;
        ls = lson[pre_], rs = rson[pre_];
        if(l == r) return ;
        int mid = (l + r) >> 1;
        if(mid >= pos) Insert(ls, lson[pre_], l, mid, pos, k);
        else Insert(rs, rson[pre_], mid + 1, r, pos, k);
    }
    int Query(int now_, int pre_, int L, int R, int l, int r) {
        if(L <= l && r <= R) return siz[now_] - siz[pre_];
        int mid = (l + r) >> 1, ans = 0;
        if(mid >= L) ans += Query(ls, lson[pre_], L, R, l, mid);
        if(mid < R) ans += Query(rs, rson[pre_], L, R, mid + 1, r);
        return ans;
    }
}

void add_edge(int from, int to) { e[++num_edge] = (edge){to, head[from]}, head[from] = num_edge; }

void dfs(int u, int fa) {
    dep[u] = dep[fa] + 1;
    dfn[u] = ++Cnt, pre[Cnt] = u, siz[u] = 1;
    for(int i = head[u]; i; i = e[i].nxt) {
        int v = e[i].to;
        if(v == fa) continue;
        dfs(v, u);
        siz[u] += siz[v];
    } 
}

int main() {
    cin >> n >> m;
    for(int i = 1, u, v; i < n; ++i) {
        cin >> u >> v;
        add_edge(u, v), add_edge(v, u);
    }
    dfs(1, 0);
    for(int i = 1; i <= n; ++i) {
        Hjt::Insert(root[i], root[i - 1], 1, n + 1, dep[pre[i]], 1);
    }
    for(int i = 1, opt, x, lst = n + 1; i <= m; ++i) {
        cin >> opt >> x;
        if(opt == 1) lst = x;
        else {
            int res = Hjt::Query(root[dfn[x] + siz[x] - 1], root[dfn[x] - 1], lst, n + 1, 1, n + 1);
            cout << res << "\n";
        }
    }
    return 0;
}

G

注意題目要求是第 \(x,y\) 個質數。

其實是一道詐騙題。

發現如果兩個數滿足相異或為 \(1\) 那么他們兩個數的值必定只差 \(1\),而這樣的質數對只有 \((2,3)\) 這一對,特判一下即可。

時間復雜度 \(\mathcal O(T)\)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM