第十四届CCPC吉林省赛题解

比赛链接

D. Trie

思路:

构建 t r i e trie trie树后建 A C AC AC自动机的 f a i l fail fail树,那么对一个串的后缀的最长公共前缀就是其在 f a i l fail fail树节点的父亲节点。那么对于询问1,假设对当前点 x 1 , x 2 , x 3 x1,x2,x3 x1,x2,x3打上新的标记,其实就相当于在 f a i l fail fail树上对于每个点以其为根的子树内打上标记。我们跑 d f s dfs dfs序,就等价于区间加标记。为了防止重复加标记,假设 x 2 x2 x2 x 1 x1 x1的子树内,那么我们只需对 x 1 x1 x1打标记即可。问题就转化为区间修改,单点查询了。

code:


#define INF 0x3f3f3f3f3f3f3f3fll
#define inf 0x3f3f3f3f
#define pb push_back
#define se second
#define fi first
#define LL long long
#define ULL unsigned long long
#define pii pair<int, int> 
#define lowbit(x) (x & (-x))
#define all(x) (x).begin(), (x).end()
#define rep(i, x, y) for(int i = (int)x; i <= (int)y; i++)
#define per(i, x, y) for(int i = (int)x; i >= (int)y; i--)
#define D(x) cout << "BUG:  " << (x) << '\n';
#define DD(x, y) cout << "BUG:  " << (x) << "  " << (y) << '\n';
template <typename T> void inline read(T& x) {
    T f = 1; x = 0; char s = getchar();
    while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
    while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
    x *= f;
}
//----------------------------------------------------------------------------------------//


const int N = 1e5 + 50;
const int M = N << 1;
const int mod = 1e9 + 7;

template<typename T>
struct BIT{
    int n;
    T t[N];

    BIT<T> () {}
    BIT<T> (int _n): n(_n) { memset(t, 0, sizeof(t)); }
    BIT<T> (int _n, T *a): n(_n) {
        memset(t, 0, sizeof(t));
        for (int i = 1; i <= n; ++ i){
            t[i] += a[i];
            int j = i + lowbit(i);
            if (j <= n) t[j] += t[i];
        }
    }
    void add(int i, T x){ 
        while (i <= n){
            t[i] += x;
            i += lowbit(i);
        }
    }
    void add(int i, int j, T x) {
        add(i, x); add(j + 1, -x);
    }
    T sum(int i){
        T ans = 0;
        while (i > 0){
            ans += t[i];
            i -= lowbit(i);
        }
        return ans;
    }
    T sum(int i, int j){
        return sum(j) - sum(i - 1);
    }
};


int tr[N][26], fail[N];
int n;
int in[N], out[N], num;
vector<int> G[N];

void dfs(int u) {
    in[u] = ++num;
    for(auto t : G[u]) dfs(t);
    out[u] = num;
}

void build() {
    queue<int> q;
    for(int i = 0; i < 26; i++) if(tr[0][i]) q.push(tr[0][i]);
    while(q.size()) {
        auto u = q.front(); q.pop();
        for(int i = 0; i < 26; i++) {
            if(tr[u][i]) {
                fail[tr[u][i]] = tr[fail[u]][i];
                q.push(tr[u][i]);
            } else tr[u][i] = tr[fail[u]][i];
        }
    }
    for(int i = 1; i < n; i++) G[fail[i]].push_back(i);
    dfs(0);
}

signed main() {
#ifdef JANGYI
    freopen("input.in", "r", stdin);
    freopen("out.out", "w", stdout);
#endif
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);

    int T;
    cin >> T;
    while(T--) {
        cin >> n;
        num = 0;
        for(int i = 0; i <= n + 5; i++) {
            G[i].clear();
            in[i] = out[i] = 0;
            fail[i] = 0;
            for(int j = 0; j < 26; j++) tr[i][j] = 0;
        }
        for(int i = 2; i <= n; i++) {
            int x; char c; 
            cin >> x >> c;
            tr[x - 1][c - 'a'] = i - 1;
        }
        build();
        BIT<int> bit(100000);
        int m; cin >> m;
        while(m--) {
            int op; cin >> op;
            if(op == 1) {
                int k; cin >> k;
                vector<int> b(k);
                for(int i = 0; i < k; i++) {
                    int x; cin >> x;
                    x--;
                    b[i] = x;
                }
                sort(all(b), [&](int i, int j) {
                    return in[i] < in[j];
                });
                int mx = -1;
                for(int i = 0; i < k; i++) {
                    if(in[b[i]] > mx) {
                        bit.add(in[b[i]], 1);
                        bit.add(out[b[i]] + 1, -1);
                    }
                    mx = max(mx, out[b[i]]);
                }
            } else {
                int x; cin >> x;
                x--;
                cout << bit.sum(in[x]) << '\n';
            }
        }
    }

    return 0;
}
/*

*/

E. Shorten the Array

思路:

对于一个区间,假设区间最小值为 m n mn mn.
1.其只出现一次,那么这个区间显然可以变为1
2.其出现多次,但是存在一个数不是 m n mn mn的倍数,那么依旧可以变为1
3.其出现多次,且其余数均为 m n mn mn的倍数,那么区间最后只能变为 ( c n t m n + 1 ) / 2 (cnt_{mn} + 1) / 2 (cntmn+1)/2

code:

signed main() {
#ifdef JANGYI
    freopen("input.in", "r", stdin);
    freopen("out.out", "w", stdout);
#endif
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);

    int T;
    cin >> T;
    while(T--) {
        int n; cin >> n;
        int cnt = 0, mn = inf;
        vector<int> a(n + 1);
        for(int i = 1; i <= n; i++) {
            int x; cin >> x;
            a[i] = x;
            if(mn > x) {
                mn = x;
                cnt = 1;
            } else if(mn == x) {
                cnt++;
            }
        }
        if(cnt == 1) {
            cout << "1\n";
            continue;
        }
        bool ok = 1;
        for(int i = 1; i <= n; i++) {
            if(a[i] % mn != 0) {
                ok = 0; 
            }
        }
        if(!ok) cout << 1 << '\n';
        else cout << (cnt + 1) / 2 << '\n';
    }

    return 0;
}
/*

*/

F. Queue

思路:

首先区间逆序对可以树状数组求出,观察到其询问的区间长度最多只有100,那么我们每次暴力判断即可。

code:

const int N = 1e5 + 50;
const int M = N << 1;
const int mod = 1e9 + 7;
 
template<typename T>
struct BIT{
    int n;
    T t[N];
 
    BIT<T> () {}
    BIT<T> (int _n): n(_n) { memset(t, 0, sizeof(t)); }
    BIT<T> (int _n, T *a): n(_n) {
        memset(t, 0, sizeof(t));
        for (int i = 1; i <= n; ++ i){
            t[i] += a[i];
            int j = i + lowbit(i);
            if (j <= n) t[j] += t[i];
        }
    }
    void add(int i, T x){ 
        while (i <= n){
            t[i] += x;
            i += lowbit(i);
        }
    }
    void add(int i, int j, T x) {
        add(i, x); add(j + 1, -x);
    }
    T sum(int i){
        T ans = 0;
        while (i > 0){
            ans += t[i];
            i -= lowbit(i);
        }
        return ans;
    }
    T sum(int i, int j){
        return sum(j) - sum(i - 1);
    }
};
 
 
 
 
signed main() {
#ifdef JANGYI
    freopen("input.in", "r", stdin);
    freopen("out.out", "w", stdout);
#endif
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
 
    int T;
    cin >> T;
    while(T--) {
        int n; cin >> n;
        vector<int> a(n + 1);
        for(int i = 1; i <= n; i++) cin >> a[i];
        LL ans = 0;
        BIT<int> bit(100001);
        for(int i = n; i >= 1; i--) {
            a[i]++;
            ans += bit.sum(a[i] - 1);
            bit.add(a[i], 1);
        }
        // D(ans)
        int m; cin >> m;
        LL mn = ans;
        while(m--) {
            int l, r; 
            cin >> l >> r;
            if(l == r) continue;
            int cnt1 = 0, cnt2 = 0;
            for(int i = l + 1; i < r; i++) {
                cnt1 += a[i] < a[l];
                cnt2 += a[i] > a[r];
            }
            ans = ans - cnt1 - cnt2;
            cnt1 = cnt2 = 0;
            for(int i = l + 1; i < r; i++) {
                cnt1 += a[i] > a[l];
                cnt2 += a[i] < a[r];
            }
            ans += cnt1 + cnt2;
            if(a[l] > a[r]) ans--;
            swap(a[l], a[r]);
            if(a[l] > a[r]) ans++;
            mn = min(ans, mn);
            // D(ans)
        }
        cout << mn << '\n';
 
    }
 
    return 0;
}

G. Matrix

思路:

显然每个点改变奇数次才有贡献。
对于点 ( i , j ) (i, j) (i,j),其改变次数 c n t = i 约数个数 ∗ j 约数个数 cnt=i_{约数个数}*j_{约数个数} cnt=i约数个数j约数个数
由数论知识易知,一个数的约数个数是奇数当且仅当其是完全平方数。
所以答案 a n s = ⌊ s q r t ( n ) ⌋ ∗ ⌊ s q r t ( m ) ⌋ ans=\lfloor sqrt(n) \rfloor * \lfloor sqrt(m) \rfloor ans=sqrt(n)⌋sqrt(m)⌋

I. World Tree

思路:

我们令 s u m 1 [ u ] sum1[u] sum1[u] u u u子树节点及其本身的 b [ i ] b[i] b[i]权值和, s u m 2 [ u ] sum2[u] sum2[u] u u u子树节点及其本身的 a [ i ] a[i] a[i]权值和.
对于当前点u,其子树内的点对其贡献显然为 a [ u ] ∗ ( s u m 1 [ u ] − b [ u ] ) a[u]*(sum1[u]-b[u]) a[u](sum1[u]b[u])
接下来计算 u u u子树外的兄弟节点的贡献。
假设 v v v u u u的一个兄弟节点。
情况1:先走 v v v,那么它们两个的贡献为 s u m 2 [ v ] ∗ s u m 1 [ u ] sum2[v]*sum1[u] sum2[v]sum1[u]
情况2:先走 u u u,贡献为 s u m 2 [ u ] ∗ s u m 1 [ v ] sum2[u]*sum1[v] sum2[u]sum1[v]
如果1>2,那么先遍历 v v v子树更优。

code:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <stack>
#include <ctime>
using namespace std;
#define INF 0x3f3f3f3f3f3f3f3fll
#define inf 0x3f3f3f3f
#define pb push_back
#define se second
#define fi first
#define LL long long
#define ULL unsigned long long
#define pii pair<int, int> 
#define lowbit(x) (x & (-x))
#define all(x) (x).begin(), (x).end()
#define rep(i, x, y) for(int i = (int)x; i <= (int)y; i++)
#define per(i, x, y) for(int i = (int)x; i >= (int)y; i--)
#define D(x) cout << "BUG:  " << (x) << '\n';
#define DD(x, y) cout << "BUG:  " << (x) << "  " << (y) << '\n';
template <typename T> void inline read(T& x) {
    T f = 1; x = 0; char s = getchar();
    while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
    while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
    x *= f;
}
//----------------------------------------------------------------------------------------//


const int N = 5e5 + 50;
const int M = N << 1;
const int mod = 1e9 + 7;

int n, a[N], b[N], sum1[N], sum2[N];
vector<int> G[N];
LL ans = 0;

void dfs1(int u, int fa) {
    sum1[u] = b[u];
    sum2[u] = a[u];
    for(int v : G[u]) {
        if(v != fa) {
            dfs1(v, u);
            sum1[u] += sum1[v];
            sum2[u] += sum2[v];
        }
    }
}
void dfs2(int u, int fa) {
    ans += 1LL * a[u] * (sum1[u] - b[u]);
    sort(all(G[u]), [](int i, int j) {
        return sum2[i] * 1LL * sum1[j] > sum2[j] * 1LL * sum1[i];
    });
    LL temp = sum1[u] - b[u];
    for(int v : G[u]) {
        if(v != fa) {
            dfs2(v, u);
            temp -= sum1[v];
            ans += 1LL * sum2[v] * temp; 
        }
    }
}
signed main() {
#ifdef JANGYI
    freopen("input.in", "r", stdin);
    freopen("out.out", "w", stdout);
#endif
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);

    int T; cin >> T;
    while(T--) {
        cin >> n;
        ans = 0;
        for(int i = 0; i <= n; i++) {
            G[i].clear();
            sum1[i] = sum2[i] = 0;
        }
        for(int i = 1; i <= n; i++) cin >> b[i];
        for(int i = 1; i <= n; i++) cin >> a[i];
        for(int i = 1; i < n; i++) {
            int x, y; cin >> x >> y;
            G[x].pb(y);
            G[y].pb(x);
        }
        dfs1(1, -1);
        dfs2(1, -1);
        cout << ans << '\n';
    }

    return 0;
}
/*

*/

M. Upanishad

思路:

区间内出现偶数次的数的异或和=区间异或和 ^ 区间内所有不同的数的异或和
前者用前缀异或和可以得到,后者是树状数组的经典 t r i c k trick trick
对于所有询问先按右端点升序排列,那么对于当前点 a [ i ] a[i] a[i],我们维护上一个 a [ i ] a[i] a[i]出现的位置: l a s t [ a [ i ] ] last[a[i]] last[a[i]].

code:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <stack>
#include <ctime>
using namespace std;
#define INF 0x3f3f3f3f3f3f3f3fll
#define inf 0x3f3f3f3f
#define pb push_back
#define se second
#define fi first
#define LL long long
#define ULL unsigned long long
#define pii pair<int, int> 
#define lowbit(x) (x & (-x))
#define all(x) (x).begin(), (x).end()
#define rep(i, x, y) for(int i = (int)x; i <= (int)y; i++)
#define per(i, x, y) for(int i = (int)x; i >= (int)y; i--)
#define D(x) cout << "BUG:  " << (x) << '\n';
#define DD(x, y) cout << "BUG:  " << (x) << "  " << (y) << '\n';
template <typename T> void inline read(T& x) {
    T f = 1; x = 0; char s = getchar();
    while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
    while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
    x *= f;
}
//----------------------------------------------------------------------------------------//


const int N = 5e5 + 50;
const int M = N << 1;
const int mod = 1e9 + 7;

template<typename T>
struct BIT{
    int n;
    T t[N];

    BIT<T> () {}
    BIT<T> (int _n): n(_n) { memset(t, 0, sizeof(t)); }
    BIT<T> (int _n, T *a): n(_n) {
        memset(t, 0, sizeof(t));
        for (int i = 1; i <= n; ++ i){
            t[i] += a[i];
            int j = i + lowbit(i);
            if (j <= n) t[j] += t[i];
        }
    }
    void add(int i, T x){ 
        while (i <= n){
            t[i] ^= x;
            i += lowbit(i);
        }
    }
    void add(int i, int j, T x) {
        add(i, x); add(j + 1, -x);
    }
    T sum(int i){
        T ans = 0;
        while (i > 0){
            ans ^= t[i];
            i -= lowbit(i);
        }
        return ans;
    }
    T sum(int i, int j){
        return sum(j) - sum(i - 1);
    }
};

int n, q, a[N], sum[N];
int ans[N];
map<int, int> last;
struct Node {
    int l, r, idx;
}p[N];

signed main() {
#ifdef JANGYI
    freopen("input.in", "r", stdin);
    freopen("out.out", "w", stdout);
#endif
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);

    cin >> n >> q;
    for(int i = 1; i <= n; i++) {
        cin >> a[i];
        sum[i] = sum[i - 1] ^ a[i];
    }
    for(int i = 1; i <= q; i++) {
        cin >> p[i].l >> p[i].r;
        p[i].idx = i;
    }
    sort(p + 1, p + 1 + q, [&](Node x, Node y) {
        return x.r <= y.r;
    });
    BIT<int> bit(N - 5);
    int l = 1;
    for(int i = 1; i <= q; i++) {
        while(l <= p[i].r) {
            if(last.count(a[l])) {
                bit.add(last[a[l]], a[l]);
            }
            last[a[l]] = l;
            bit.add(l, a[l]);
            l++;
        }
        int res = sum[p[i].r] ^ sum[p[i].l - 1];
        res = res ^ (bit.sum(p[i].r) ^ bit.sum(p[i].l - 1));
        ans[p[i].idx] = res;
    }
    for(int i = 1; i <= q; i++) cout << ans[i] << '\n';


    return 0;
}
/*

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值