zoukankan      html  css  js  c++  java
  • 模拟赛小结:Gym 102800 The 14th Jilin Provincial Collegiate Programming Contest

    比赛链接:传送门

    阴差阳错地又开始训练了。

    lh考研去了,我和hat又坑到了另一个很强的学弟guapi组队。The 1226465-th prime number 又回来啦。

    我和guapi差不多停训了一年,hat倒是一直在训练,前段时间cf还上了橙,Orz。

     

    回来第一场先练了个吉林的省赛热热手(省赛的水题还是挺多的,做得很开心),罚时放到现场赛居然还能拿个亚军。


     

    Problem A. Chord 00:15 (+) Solved by xk (小模拟)

    代码:

    #include <bits/stdc++.h>
    #define eps 1e-6
    #define ls (rt << 1)
    #define rs (rt << 1 | 1)
    #define lowbit(x) (x & (-x))
    #define SZ(v) ((int)(v).size())
    #define All(v) (v).begin(), (v).end()
    #define mp(x, y) make_pair(x, y)
    #define fast ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
    #define endl '
    '
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    typedef pair<int, int> P;
    const int N = 1e3 + 10;
    const int mod = 1e9 + 7;
    const int inf = 0x3f3f3f3f;
    int main()
    {
        map<string, int> mp;
        mp["C"] = 1;
        mp["C#"] = 2;
        mp["D"] = 3;
        mp["D#"] = 4;
        mp["E"] = 5;
        mp["F"] = 6;
        mp["F#"] = 7;
        mp["G"] = 8;
        mp["G#"] = 9;
        mp["A"] = 10;
        mp["A#"] = 11;
        mp["B"] = 12;
        fast;
        int T;
        cin >> T;
        while(T--)
        {
            string a, b, c;
            cin >> a >> b >> c;
            int x = mp[a], y = mp[b], z = mp[c];
            if(x > y) y += 12;
            if(y > z) z += 12;
            if(y - x == 4 && z - y == 3) cout << "Major triad
    ";
            else if(y - x == 3 && z - y == 4) cout << "Minor triad
    ";
            else cout << "Dissonance
    ";
        }
    }
    View Code

     

    Problem B. Problem Select 00:06 (+) Solved by Guapi (排序 签到)

    代码:

    #include <bits/stdc++.h>
    #define eps 1e-6
    #define ls (rt << 1)
    #define rs (rt << 1 | 1)
    #define lowbit(x) (x & (-x))
    #define SZ(v) ((int)(v).size())
    #define All(v) (v).begin(), (v).end()
    #define mp(x, y) make_pair(x, y)
    #define fast ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    typedef pair<int, int> P;
    const int N = 1e3 + 10;
    const int mod = 1e9 + 7;
    const int inf = 0x3f3f3f3f;
    int n, k, a[N];
    char s[N];
    int main()
    {
        int t;
        scanf("%d", &t);
        while(t --)
        {
            scanf("%d%d", &n, &k);
            for(int i = 1; i <= n; ++ i) {
                scanf("%s", s + 1);
                int len = strlen(s + 1);
                int j = len;
                while(j > 0 && s[j] != '/') -- j;
                int x = 0;
                for(j = j + 1; j <= len; ++ j) {
                    x = x * 10 + s[j] -'0';
                }
                a[i] = x;
            }
            sort(a + 1, a + n + 1);
            for(int i = 1; i <= k; ++ i) {
                printf("%d ", a[i]);
            }
            puts("");
        }
        return 0;
    }
    View Code

     

    Problem C. String Game 00:32 (+1) Solved by Dancepted (dp 背包)

    题目大意:

      给两个字符串s,t,求字符串s有多少个子序列和t完全相同。

    解法:

      用$cnt_{j}$表示t的长度为j的前缀能有多少个匹配。那么在后面出现$s_{i}=t_{j+1}$时,长度为j+1的前缀就会增加cnt_{j}个匹配。

      是一个类似01背包的转移,所以第二个循环应该倒着跑。

    代码:$O(nm)$

    #include <bits/stdc++.h>
    #define sz(x) ((int)x.size())
    #define fast ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
    #define endl '
    '
    #define N 5005
    #define M 1005
    
    using namespace std;
    typedef long long ll;
    
    ll cnt[M];
    ll md = 1000000007;
    int main(){
        fast;
        string s, t;
        while (cin >> s >> t) {
            memset(cnt, 0, (sz(t) + 1) * sizeof(ll));
            cnt[0] = 1;
            for (int i = 0; i < sz(s); i++) {
                for (int j = sz(t)-1; j >= 0; j--) {
                    if (s[i] == t[j]) {
                        cnt[j+1] = (cnt[j+1] + cnt[j]) % md;
                    }
                }
            }
            cout << cnt[sz(t)] << endl;
        }
        return 0;
    }
    View Code

     

    Problem D. Trie (待补)

      hat的补题代码(做法大概是AC自动机fail树上用树状数组搞一下差分什么的)

    #include<bits/stdc++.h>
    #define forn(i, n) for (int i = 0; i < (n); i++)
    #define forab(i, a, b) for (int i = (a); i <= (b); i++)
    #define forba(i, b, a) for (int i = (b); i >= (a); i--)
    #define mset(a, x, n) memset(a, x, sizeof(a[0]) * (n))
    #define updiv(x, y) (((x) + (y) - 1) / (y)) // y > 0
    #define pb(x) push_back(x)
    #define eb emplace_back
    #define mp(x, y) make_pair(x, y)
    #define fi first
    #define se second
    #define sz(x) ((int)x.size())
    #define all(x) (x).begin(), (x).end()
    #define endl '
    '
    #ifdef hat
    #define fast
    #define de(x) cout  << #x << '=' << (x) << ' '
    #define dee(x) cout  << #x << '=' << (x) << endl
    #else
    #define fast ios::sync_with_stdio(0), cin.tie(0)
    #define de(x) ((void) 0)
    #define dee(x) ((void) 0)
    #endif
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    typedef double db;
    typedef pair<int, int> pii;
    typedef vector<int> VI;
    
    const int maxn = 1e5 + 5;
    const int mod = 998244353;
    const int INF = 0x3f3f3f3f;
    const ll llINF = 0x3f3f3f3f3f3f3f3f;
    ll make_compiler_happy() {return INF & maxn & mod & llINF;}
    ll fpow(ll a,ll b) {ll res=1;a%=mod; assert(b>=0); for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
    ll inv(ll x) {return fpow(x, mod-2);}
    
    int ch[maxn][26];
    int n;
    int f[maxn];
    vector<int> g[maxn];
    
    void getfail()
    {
        queue<int> q;
        f[0] = 0;
        for(int c = 0; c < 26; c++)
        {
            int u = ch[0][c];
            if(u) {
                f[u] = 0;
                q.push(u);
            }
        }
        while(!q.empty())
        {
            int r = q.front(); q.pop();
            for(int c = 0; c < 26; c++)
            {
                int u = ch[r][c];
                if(!u) continue;
                q.push(u);
                int v = f[r];
                while(v && !ch[v][c]) v = f[v];
                f[u] = ch[v][c];
            }
        }
        for(int i = 1; i < n; i++)
        {
            g[f[i]].push_back(i);
        }
    }
    
    int cur;
    int in[maxn], out[maxn];
    int dep[maxn];
    
    void dfs1(int u, int d)
    {
        dep[u] = d;
        in[u] = ++cur;
        for(int v : g[u])
        {
            dfs1(v, d + 1);
        }
        out[u] = cur;
    }
    
    int sum[maxn * 4];
    #define mid (l+r)/2
    #define ls o*2
    #define rs o*2+1
    
    void add(int o, int l, int r, int ql, int qr)
    {
        if(ql <= l && r <= qr) {
            sum[o]++;
            return;
        }
        if(ql > r || qr < l) {
            return;
        }
        add(ls, l, mid, ql, qr);
        add(rs, mid+1, r, ql, qr);
    }
    
    int bit[maxn];
    
    void bitadd(int x, int d)
    {
        for(; x <= cur + 1; x += x & -x) {
            bit[x] += d;
        }
    }
    
    int bitsum(int x)
    {
        int s = 0;
        for(; x; x -= x & -x) {
            s += bit[x];
        }
        return s;
    }
    
    void add(int x)
    {
        add(1, 1, cur, in[x], out[x]);
    }
    
    int query(int o, int l, int r, int x, int s)
    {
        if(l == r) return s + sum[o];
        if(x <= mid) return query(ls, l, mid, x, s + sum[o]);
        else return query(rs, mid + 1, r, x, s + sum[o]);
    }
    
    int main() 
    {
        fast;
        int T;
        cin >> T;
        while(T--)
        {
            cin >> n;
            memset(sum, 0, sizeof(sum));
            for(int i = 0; i < n; i++)
            {
                g[i].clear();
                memset(ch[i], 0, sizeof(ch[i]));
            }
            for(int i = 1; i < n; i++)
            {
                int u; char c;
                cin >> u >> c;
                u--;
                ch[u][c-'a'] = i;
            }
            
            cur = 0;
            getfail();
            dfs1(0, 0);
            int m;
            cin >> m;
            while(m--)
            {
                int t;
                cin >> t;
                if(t == 1) {
                    int k;
                    cin >> k;
                    vector<int> v(k);
                    for(int i = 0; i < k; i++)
                    {
                        cin >> v[i];
                        v[i]--;
                    }
                    sort(v.begin(), v.end(), [&](int x, int y) {return dep[x] < dep[y];});
                    vector<int> tmp;
                    for(int i : v)
                    {
                        if(bitsum(in[i])) {
                            continue;
                        }
                        add(i);
                        tmp.push_back(i);
                        bitadd(in[i], 1);
                        bitadd(out[i] + 1, -1);
                    }
                    for(int i : tmp)
                    {
                        bitadd(in[i], -1);
                        bitadd(out[i] + 1, 1);
                    }
                    // forn(i, n)
                    // {
                    //     cout << query(1, 1, cur, in[i], 0) << ' ';
                    // }
                    // cout << endl;
                }
                else {
                    int x;
                    cin >> x;
                    x--;
                    cout << query(1, 1, cur, in[x], 0) << endl;
                }
            }
        }
    }
    View Code

     

    Problem E. Shorten the Array 00:51 (+) Solved by Dancepted&hat (思维题)

      看题目发呆了好久,后来脑门一拍想了个结论就过了。

    题目大意:

      给出一个长度为n(n <= 1e6)的数组,每次可以选择两个位置相邻的数a、b(要求a和b都大于0),用a%b或者b%a替换他们。

    思路:
      设最小的数是mn,数量有cnt个。

      如果cnt = 1,显然可以通过(ai, mn)=(mn % ai)= (mn),来删去所有的点,答案就是1。

      如果cnt ≠ 1,则需要讨论一下。

    两个结论:

      1、若所有的数都是mn的倍数,则答案为$left lceil frac{cnt}{2} ight ceil$

      2、若存在一个数x不是mn的倍数,设y为与mn相邻的一个数则必定可以通过(mn, y) = (mn % y),把mn换到x的旁边,然后(mn, x)=(x%mn),且x%mn < mn,之后就可以用x%mn消去所有的

    代码:

    #include <bits/stdc++.h>
    #define eps 1e-6
    #define ls (rt << 1)
    #define rs (rt << 1 | 1)
    #define lowbit(x) (x & (-x))
    #define SZ(v) ((int)(v).size())
    #define All(v) (v).begin(), (v).end()
    #define mp(x, y) make_pair(x, y)
    #define fast ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
    #define endl '
    '
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    typedef pair<int, int> P;
    const int N = 1e6 + 10;
    const int mod = 1e9 + 7;
    const int inf = 0x3f3f3f3f;
    
    int n;
    int a[N];
    void solveTestCase() {
        cin >> n;
        int mn = 1e9 + 5, cnt = 0;
        for (int i = 1; i <= n; i++) {
            cin >> a[i];
            if (mn > a[i]) {
                mn = a[i];
                cnt = 1;
            }
            else if (mn == a[i]) {
                cnt++;
            }
        }
        bool isOne = false;
        for (int i = 1; i <= n; i++) {
            if (a[i] % mn != 0) {
                isOne = true;
                break;
            }
        }
        if (isOne)
            cout << 1 << endl;
        else
        {
            cout << (cnt + 1) / 2 << endl;
        }
        
    }
    
    int main() {
        fast;
        int t;
        cin >> t;
        while (t--) {
            solveTestCase();
        }
        return 0;
    }
    View Code

     

    Problem F. Queue 04:11 (+5) Solved by Guapi (树状数组+暴力,树套树)

      没看到$p_{i2}−p_{i1} ≤ 100$,用树套树写了半天没调出来的样子,后来看到了这个条件就暴力过了。

      因为太久做题,出现了一些RE,MLE之类的错误,多了不少罚时。

    题目大意:

      动态逆序对,每次操作是交换两个数的位置,或询问逆序对的数量。

    解法:

      树套树经典题,但这个两个数的位置距离不超过100,也可以树状数组+暴力。

    比赛代码(树状数组+暴力):

    #include <bits/stdc++.h>
    using namespace std;
    #define lowbit(x) (x&(-x))
    typedef long long ll;
    const int N = 1e5 + 10;
    const int M = 2500000;
    int a[N], n, m;
    int c[N];
    void add(int x) {
        for(; x <= N - 5;  x += lowbit(x)) {
            c[x] ++ ;
        }
    }
    int ask(int x) {
        int res = 0;
        for(; x > 0; x -= lowbit(x)) {
            res += c[x];
        }
        return res;
    }
    int main()
    {
        int T;
        scanf("%d", &T);
        while(T--)
        {
            ll cnt = 0;
            scanf("%d", &n);
            memset(c, 0, sizeof(c));
            for(int i = 1; i <= n; ++ i) {
                scanf("%d", &a[i]);
                ++ a[i];
                cnt += i - 1 - ask(a[i]);
                add(a[i]);
            }
            ll ans = cnt;
            scanf("%d", &m);
            int L, R;
            while(m --)
            {
                scanf("%d%d", &L, &R);
                if(L == R) continue;
                int cnt1 = 0, cnt2 = 0;
                for(int j = L + 1; j <= R - 1; ++ j) {
                    if(a[j] < a[L]) ++ cnt1;
                    if(a[j] > a[R]) ++ cnt2;
                }
                cnt = cnt - cnt1 - cnt2;
                cnt1 = cnt2 = 0;
                for(int j = L + 1; j <= R - 1; ++ j) {
                    if(a[j] > a[L]) ++ cnt1;
                    if(a[j] < a[R]) ++ cnt2;
                }
                cnt = cnt + cnt1 + cnt2;
                if(a[L] > a[R]) -- cnt;
                swap(a[L], a[R]);
                if(a[L] > a[R]) ++ cnt;
                ans = min(ans, cnt);
            }
            printf("%lld
    ", ans);
        }
        return 0;
    }
    View Code

    树套树代码:

    #include <bits/stdc++.h>
    using namespace std;
    #define lowbit(x) (x&(-x))
    typedef long long ll;
    const int N = 1e5 + 10;
    const int M = 2500000;
    int a[N], tot, n, m, mx;
    struct tree{
        int lc, rc, cnt;
    }T[N << 7];
    int root[N];//正常树的根节点
    int ux[50], uy[50], numx, numy; //保存树状数组的节点
    int S[N];//树状数组根节点
    void build(int &rt, int l, int r)//建树
    {
        rt = ++ tot;
        T[rt].cnt = 0;
        T[rt].lc = T[rt].rc = 0;
        if (l == r) return;
        int mid = (l + r) >> 1;
        build(T[rt].lc, l, mid);
        build(T[rt].rc, mid + 1, r);
    }
    void UP(int &cur, int l, int r, int pos, int val)//节省内存
    {
        if (!cur) cur = ++ tot;
        T[cur].cnt += val;
        if (l == r) return;
        int mid = (l + r) >> 1;
        if (pos <= mid) UP(T[cur].lc, l, mid, pos, val);
        else UP(T[cur].rc, mid + 1, r, pos, val);
    }
    void update(int &cur, int pre, int l, int r, int pos, int val)
    {
        cur = ++tot;//新树必须新开节点
        T[cur] = T[pre];
        T[cur].cnt += val;
        if (l == r) return;
        int mid = (l + r) >> 1;
        if (pos <= mid) update(T[cur].lc, T[pre].lc, l, mid, pos, val);
        else update(T[cur].rc, T[pre].rc, mid + 1, r, pos, val);
    }
    void clear(int &cur, int l, int r)
    {
        T[cur].cnt = 0;
        if(!cur || l == r) return ;
        int mid = (l + r) >> 1;
        if(T[cur].lc) clear(T[cur].lc, l, mid);
        if(T[cur].rc) clear(T[cur].rc, mid + 1, r);
        cur = 0;
    }
    
    void add(int pos, int val)//用树状数组更新
    {
        int x = a[pos];
        for (; pos <= n; pos += lowbit(pos))
            UP(S[pos], 0, mx, x, val);
    }
    
    int query(int l, int r, int L, int R, int x, int y) {
        if(x > y) return 0;
        if (x <= l && r <= y)
        {
            int ans = T[R].cnt - T[L].cnt;
            for (int i = 0; i < numy; i++) ans += T[uy[i]].cnt;
            for (int i = 0; i < numx; i++) ans -= T[ux[i]].cnt;
            return ans;
        }
        int  Ux[50], Uy[50];
        for (int i = 0; i < numy; i++) Uy[i] = uy[i];
        for (int i = 0; i < numx; i++) Ux[i] = ux[i];
        int mid = (l + r) >> 1;
        int sum = 0;
        if (x <= mid)
        {
            for (int i = 0; i < numy; i++) uy[i] = T[Uy[i]].lc;
            for (int i = 0; i < numx; i++) ux[i] = T[Ux[i]].lc;
            sum = query(l, mid, T[L].lc, T[R].lc, x, y);
        }
        if (y > mid)
        {
            for (int i = 0; i < numy; i++) uy[i] = T[Uy[i]].rc;
            for (int i = 0; i < numx; i++) ux[i] = T[Ux[i]].rc;
            sum += query(mid + 1, r, T[L].rc, T[R].rc, x, y);
        }
        return sum;
    }
    
    int Q(int L)
    {
        int cnt = 0;
        // left, large than a[L]
        if(L > 1) {
            numx = 0, numy = 0;
            for (int j = 0; j > 0; j -= lowbit(j)) ux[numx++] = S[j];
            for (int j = L - 1; j > 0; j -= lowbit(j)) uy[numy++] = S[j];
            cnt += query(0, mx, root[0], root[L - 1], a[L] + 1, mx);
        }
        // right, less than a[L]
        if(L < n) {
            numx = 0, numy = 0;
            for (int j = L; j > 0; j -= lowbit(j)) ux[numx++] = S[j];
            for (int j = n; j > 0; j -= lowbit(j)) uy[numy++] = S[j];
            cnt += query(0, mx, root[L], root[n], 0, a[L] - 1);
        }
        return cnt;
    }
    
    int main()
    {
        int t;
        scanf("%d", &t);
        while(t --)
        {
            tot = 0;
            ll cnt = 0;
            scanf("%d", &n);
            mx = 0;
            for(int i = 1; i <= n; ++ i) {
                scanf("%d", &a[i]);
                mx = max(mx, a[i]);
            }
            build(root[0], 0, mx);
            numx = 0, numy = 0;
            for(int i = 1; i <= n; ++ i) {
                S[i] = 0;
                update(root[i], root[i - 1], 0, mx, a[i], 1);
                cnt += query(0, mx, root[0], root[i - 1], a[i] + 1, mx);
            }
            ll ans = cnt;
            int L, R;
            scanf("%d", &m);
            while(m --)
            {
                scanf("%d%d", &L, &R);
                if(L == R) continue;
                int delta = 0;
                delta -= Q(L) + Q(R);
                if(a[L] > a[R]) delta ++;
                add(L, -1);
                add(R, -1);
                swap(a[L], a[R]);
                add(L, 1);
                add(R, 1);
                delta += Q(L) + Q(R);
                if(a[L] > a[R]) delta --;
                cnt += delta;
                ans = min(ans, cnt);
            }
            printf("%lld
    ", ans);
            for(int i = 1; i <= tot; ++ i) {
                T[i].cnt = T[i].lc = T[i].rc = 0;
            }
        }
        return 0;
    }
    View Code

     

    Problem G. Matrix 01:18 (+) Solved by Dancepted&hat (数论,思维题)

    思路:

      翻转次数为奇数的情况下,才对最终的答案有贡献。

      (x,y)的翻转次数 = x约数的个数 × y的约数的个数。

      设$x = prod p_{i}^{t_{i}}(p_{i}是质数)$,则约数个数$d(x) = prod(t_{i}+1)$,所有的$t_{i}$都必须是偶数,才对最终的答案有贡献。

      也就是说x和y是完全平方数。$[1, n]$区间内的完全平方数的平方根的范围为$[1, left lfloor sqrt{n} ight floor]$。

    解法:

      答案就是$left lfloor sqrt{n} ight floor * left lfloor sqrt{m} ight floor$。

    代码:

    #include <bits/stdc++.h>
    #define eps 1e-6
    #define ls (rt << 1)
    #define rs (rt << 1 | 1)
    #define lowbit(x) (x & (-x))
    #define SZ(v) ((int)(v).size())
    #define All(v) (v).begin(), (v).end()
    #define mp(x, y) make_pair(x, y)
    #define fast ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
    #define endl '
    '
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    typedef pair<int, int> P;
    const int N = 1e6 + 10;
    const int mod = 1e9 + 7;
    const int inf = 0x3f3f3f3f;
    
    ll sq(ll x)
    {
        ll t = sqrt(x) - 1;
        if(t < 0) t = 0;
        while((t + 1) * (t + 1) <= x) t++;
        return t;
    }
    
    int main() {
        fast;
        int t;
        cin >> t;
        while (t--) {
            ll n, m;
            cin >> n >> m;
            cout << sq(n) * sq(m) << endl;
        }
        return 0;
    }
    View Code

     

    Problem H. Curious 03:43 (+1) Solved by Dancepted&hat (数论 容斥 莫比乌斯函数)

    需要使劲分析复杂度的一道数论题。

    WA了一发是因为加记忆化的时候,忘记用long long了。

    题目大意:

      给出一个长度为n的数列a,满足$a_{i}<=m$,k次询问,每次给出一个x,回答:

      $sum_{i=1}^{n}sum_{j=1}^{n}[gcd(a_{i}, a_{j})==x]$

    思路:

      用$sum_{i}$表示数列中为i的倍数的数的数量。

      ①对于x = 1的情况:

      $sum_{i=1}^{n}sum_{j=1}^{n}[gcd(a_{i}, a_{j})==1]$

      所有1的倍数的gcd至少为1,这些数的数量就是$sum_{1}*sum_{1}$,但这里面要减去gcd为2、3、5...的那些数,但这样6、10、15之类的又被多减了。

      实际上$sum_{i}*sum_{i}$的系数正好是莫比乌斯函数$mu(i)$。

      因此对于x = 1,有$ans = sum_{m}^{i=1}mu (i)sum_{i}^2$。

      预处理sum_{i}时,可以先O(n)地统计[1, m]内每个数的出现次数,然后用埃氏筛在O(mloglogm)的时间内计算sum。

      总的时间复杂度是O(n+mloglogm)的。

      ②同样的,对于一个给定的x,有:

      $sum_{i=1}^{n}sum_{j=1}^{n}[gcd(a_{i}, a_{j})==x] = sum_{i=1}^{n}sum_{j=1}^{n}[gcd(frac{a_{i}}{x}, frac{a_{j}}{x})==1]$。

      把数列中所有x的倍数的数单独拿出来,并除x。再跑一遍①中的算法,就可以得到答案了,令m' = m / x,时间复杂度为$O(n + m'loglogm)。

      这样看起来总复杂度似乎高达$O(n*n + n*mloglogm))$。

      复杂度右边的部分实际上是$sum^{m}_{x=1}frac{m}{x}loglog(m/x) <=mlogmsum^{m}_{x=1}frac{1}{x}$,而$frac{1}{x}$的上界是一个常数(百度了一下好像是$frac{pi^2}{6}$)

      并且,数列中的每个数,只会被①调用$d(a_{i})$次($d(n)$表示n的约数的个数),而d(n)的一个显然的上界是$2sqrt{n}$,复杂度的左边实际上是$O(nsqrt{n})$。

      对于重复的x,也可以记忆化。

    代码:$O(nsqrt{n}+mloglogm))$

    #include <bits/stdc++.h>
    #define eps 1e-6
    #define ls (rt << 1)
    #define rs (rt << 1 | 1)
    #define lowbit(x) (x & (-x))
    #define SZ(v) ((int)(v).size())
    #define All(v) (v).begin(), (v).end()
    #define mp(x, y) make_pair(x, y)
    #define fast ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
    #define endl '
    '
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    typedef pair<int, int> P;
    const int N = 1e5 + 10;
    const int mod = 1e9 + 7;
    const int inf = 0x3f3f3f3f;
    
    int a[N];
    vector<int> V[N];
    int prime[N], miu[N];
    int cnt[N], sum[N];
    
    ll solve(vector<int> &v) {
        int n = SZ(v);
        int m = 0;
        for(int i = 0; i < n; i++) {
            m = max(m, v[i]);
        }
        memset(cnt, 0, sizeof(int) * (m + 1));
        for(int i = 0; i < n; i++)
            cnt[v[i]]++;
        memset(sum, 0, sizeof(int) * (m + 1));
        // ll ans = (ll) n * n;
        ll ans = 0;
        for(int i = 1; i <= m; i++)
        {
            for(int j = i; j <= m; j += i) {
                sum[i] += cnt[j];
            }
            ans += (ll) miu[i] * sum[i] * sum[i];
        }
        return ans;
    }
    
    void solveTestCase() {
        int n, m, k;
        cin >> n >> m >> k;
        for(int i = 1; i <= m; i++) {
            V[i].clear();
            cnt[i] = 0;
        }
        for(int i = 1; i <= n; i++) {
            cin >> a[i];
            cnt[a[i]]++;
        }
        for(int i = 1; i <= m; i++) {
            for(int j = i; j <= m; j+=i) {
                for(int _ = 0; _ < cnt[j]; _++) {
                    V[i].push_back(j / i);
                }
            }
        }
        map<int, ll> mp;
        for(int i = 0; i < k; i++) {
            int x;
            cin >> x;
            if(x > m || x <= 0) cout << 0 << endl;
            else {
                if(mp.count(x)) {
                    cout << mp[x] << endl;
                }
                else {
                    ll t = solve(V[x]);
                    mp[x] = t;
                    cout << t << endl;
                }
            }
        }
    }
    void getPrime_and_Miu() {
        miu[1] = 1;
        for (int i = 2; i <= N; i++) {
            if (!prime[i]) prime[++prime[0]] = i, miu[i] = -1;
            for (int j = 1; j <= prime[0] && prime[j] <= N/i; j++) {
                prime[i*prime[j]] = 1;
                miu[i*prime[j]] = miu[i] * (i%prime[j] ? -1 : 0);
                if (i%prime[j] == 0) break;
            }
        }
    }
    int main() {
        fast;
        getPrime_and_Miu();
        int t; cin >> t;
        while (t--) {
            solveTestCase();
        }
        return 0;
    }
    View Code

     

    Problem I. World Tree 04:05 (+) Solved by Dancepted (经典贪心 + 树形dp)

    思路:

      题目中有说到,如果当前节点存在孩子,就不能返回到父节点,换句话说就是在树上dfs。

      所以如果有策略可以确定dfs的顺序,那么这题就解决了。

      令$A_{u} = sum_{vin以u为根的子树}a_{v}$,$B_{u} = sum_{vin以u为根的子树}b_{v}$

      以节点$u$为根的子树,对答案的贡献可以分成两类:

      1、子树的根$u$与子树内所有其他节点之间产生的贡献,这部分是无法改变的,为$a_{u}*(B_{u} - b_{u})$;

      2、以$u$为根的子树,与以$u$的兄弟节点$u'$及其子树产生的贡献,有两种可能的情况:

        ①$A_{u}B_{u'}$,此时是先遍历u子树,再遍历u'子树;

        ②$A_{u'}B_{u}$,此时是先遍历u'子树,再遍历u子树;

      如果①>②,说明先遍历u子树更优,反之亦然。

      在dfs到下一层之前,根据这个规则对子节点排序后再遍历就可以了。

      这个贪心策略之前在CF上做过类似的题

    代码:O(nlogn)

    #include <bits/stdc++.h>
    #define eps 1e-6
    #define ls (rt << 1)
    #define rs (rt << 1 | 1)
    #define lowbit(x) (x & (-x))
    #define SZ(v) ((int)(v).size())
    #define All(v) (v).begin(), (v).end()
    #define mp(x, y) make_pair(x, y)
    #define fast ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
    #define endl '
    '
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    typedef pair<int, int> P;
    const int N = 1e5 + 10;
    const int mod = 1e9 + 7;
    const int inf = 0x3f3f3f3f;
    
    int n;
    int a[N], b[N];
    ll sa[N], sb[N];
    vector<int> es[N];
    ll ans = 0;
    
    void dfsInit(int u, int fa) {
        sa[u] = a[u];
        sb[u] = b[u];
        for (int v : es[u]) if (v != fa) {
            dfsInit(v, u);
            sa[u] += sa[v];
            sb[u] += sb[v];
        }
    }
    bool cmp(int l, int r) {
        return sa[l]*sb[r] > sa[r]*sb[l];
    }
    void dfsSolve(int u, int fa) {
        ans += (ll)a[u]*(sb[u] - b[u]);
    
        sort(es[u].begin(), es[u].end(), cmp);
        ll tmp = sb[u] - b[u];
        for (int v : es[u]) if (v != fa) {
            dfsSolve(v, u);
            tmp -= sb[v];
            ans += sa[v] * tmp;
        }
    }
    void solveTestCase() {
        cin >> n;
        for (int i = 1; i <= n; i++) es[i].clear();
        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-1; i++) {
            int u, v; cin >> u >> v;
            es[u].push_back(v);
            es[v].push_back(u);
        }
        dfsInit(1, -1);
        ans = 0;
        dfsSolve(1, -1);
        cout << ans << endl;
    }
    
    int main() {
        fast;
        int t; cin >> t;
        while (t--) {
            solveTestCase();
        }
        return 0;
    }
    View Code

     

    Problem J. Situation 01:55 (+1) Solved by hat(min-max)

    少开了一个状态,WA了一发。

    思路:

      总状态数是$3^{9}*2approx 4*10^{4}$。可以dfs加剪枝,dfs中用min-max决策。

    代码:

    #include <bits/stdc++.h>
    #define eps 1e-6
    #define ls (rt << 1)
    #define rs (rt << 1 | 1)
    #define lowbit(x) (x & (-x))
    #define SZ(v) ((int)(v).size())
    #define All(v) (v).begin(), (v).end()
    #define mp(x, y) make_pair(x, y)
    #define fast ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
    #define endl '
    '
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    typedef pair<int, int> P;
    const int N = 1e3 + 10;
    const int mod = 1e9 + 7;
    const int inf = 0x3f3f3f3f;
    
    struct state{
        int a[3][3];
        int toint()
        {
            int p3 = 1;
            int res = 0;
            for(int i = 0; i < 3; i++)
            {
                for(int j = 0; j < 3; j++)
                {
                    res += a[i][j] * p3;
                    p3 *= 3;
                }
            }
            return res;
        }
        bool isend()
        {
            for(int i = 0; i < 3; i++)
            {
                for(int j = 0; j < 3; j++)
                {
                    if(!a[i][j]) return 0;
                }
            }
            return 1;
        }
        int cal()
        {
            int t = 0;
            for(int i = 0; i < 3; i++)
            {
                int f = a[i][0];
                for(int j = 1; j < 3; j++)
                {
                    if(a[i][j] != f) f = 0;
                }
                if(f) {
                    if(f == 1) t++;
                    else t--;
                }
            }
            for(int i = 0; i < 3; i++)
            {
                int f = a[0][i];
                for(int j = 1; j < 3; j++)
                {
                    if(a[j][i] != f) f = 0;
                }
                if(f) {
                    if(f == 1) t++;
                    else t--;
                }
            }
            int f = a[0][0];
            for(int j = 1; j < 3; j++)
            {
                if(a[j][j] != f) f = 0;
            }
            if(f) {
                if(f == 1) t++;
                else t--;
            }
            f = a[0][2];
            for(int j = 1; j < 3; j++)
            {
                if(a[j][2-j] != f) f = 0;
            }
            if(f) {
                if(f == 1) t++;
                else t--;
            }
            return t;
        }
    };
    
    int res[30000][2];
    
    int dfs(state s, int ismax)
    {
        int x = s.toint();
        if(res[x][ismax] != -1000) {
            return res[x][ismax];
        }
        if(s.isend()) {
            return res[x][ismax] = s.cal();
        }
        if(ismax) 
        { // O:1
            for(int i = 0; i < 3; i++)
                for(int j = 0; j < 3; j++)
                    if(s.a[i][j] == 0)
                    {
                        state t = s;
                        t.a[i][j] = 1;
                        res[x][ismax] = max(dfs(t, !ismax), res[x][ismax]);
                    }
        }
        else
        { // X:2
            res[x][ismax] = 1000;
            for(int i = 0; i < 3; i++)
                for(int j = 0; j < 3; j++)
                    if(s.a[i][j] == 0)
                    {
                        state t = s;
                        t.a[i][j] = 2;
                        res[x][ismax] = min(dfs(t, !ismax), res[x][ismax]);
                    }
        }
        return res[x][ismax];
    }
    
    int main() {
        for(int i = 0; i < 30000; i++)
            res[i][0] = res[i][1] = -1000;
        int t;
        cin >> t;
        while (t--) {
            int ismax;
            cin >> ismax;
            state t;
            for(int i = 0; i < 3; i++)
            {
                string s;
                cin >> s;
                for(int j = 0; j < 3; j++){
                    if(s[j] == '.') t.a[i][j] = 0;
                    else if(s[j] == 'O') t.a[i][j] = 1;
                    else t.a[i][j] = 2;
                }
            }
            cout << dfs(t, ismax) << endl;
        }
        return 0;
    }
    View Code

     

    Problem L. Swimmer 00:30 (+1) Solved by Guapi (签到)

    RE了一发是因为数组开小了。。。

    代码:

    #include <bits/stdc++.h>
    #define eps 1e-6
    #define ls (rt << 1)
    #define rs (rt << 1 | 1)
    #define lowbit(x) (x & (-x))
    #define SZ(v) ((int)(v).size())
    #define All(v) (v).begin(), (v).end()
    #define mp(x, y) make_pair(x, y)
    #define fast ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
    #define endl '
    '
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    typedef pair<int, int> P;
    const int N = 1e6 + 10;
    const int mod = 1e9 + 7;
    const int inf = 0x3f3f3f3f;
    int n, m, q, a[N];
    int main()
    {
        scanf("%d%d%d", &n, &m, &q);
        for(int i = 1; i <= n; ++ i) {
            scanf("%d", &a[i]);
        }
        int p, k;
        for(int i = 1; i <= q; ++ i) {
            scanf("%d%d", &p, &k);
            int y = (1LL * p * a[k] ) % (2 * m);
            if(y <= m) printf("%d
    ", y);
            else printf("%d
    ", 2 * m - y);
        }
    
        return 0;
    }
    View Code

     

    Problem M. Upanishad 04:38 (+) Solved by Guapi (树状数组)

    知道是热身赛的题,就没急着做。

    思路:

      区间出现次数为偶数的异或和 = 区间出现过的数的异或和 xor 区间出现次数为奇数的异或和。

      出现次数为奇数的异或和比较简单,用异或和的前缀和算一算就可以了。(出现次数为偶数的异或和为0)

      区间出现过的数,可以这么求(好像是树状数组的一个trick?):遍历的时候如果一个数之前出现过,那么就把树状数组中,在之前出现的位置减去这个数,再在新的位置加上这个数。

    代码:O(nlogn)

    #include <bits/stdc++.h>
    using namespace std;
    #define lowbit(x) (x&(-x))
    typedef long long ll;
    const int N = 5e5 + 10;
    const int M = 2500000;
    int a[N], sum[N], n, q, ans[N];
    int c[N];
    map<int, int> mp;
    struct node {
        int l, r, pos;
        bool operator<(const node &o)const {
            return r < o.r;
        }
    };
    node Q[N];
    void add(int x, int val) {
        for(; x <= n;  x += lowbit(x)) {
            c[x] ^= val ;
        }
    }
    int ask(int x) {
        int res = 0;
        for(; x > 0; x -= lowbit(x)) {
            res ^= c[x];
        }
        return res;
    }
    int main()
    {
        scanf("%d%d", &n, &q);
        for(int i = 1; i <= n; ++ i) {
            scanf("%d", &a[i]);
            sum[i] = a[i] ^ sum[i - 1];
        }
        for(int i = 1; i <= q; ++ i) {
            scanf("%d%d", &Q[i].l, &Q[i].r);
            Q[i].pos = i;
        }
        sort(Q + 1, Q + q + 1);
        int L = 1;
        for(int i = 1; i <= q; ++ i) {
            while(L <= Q[i].r) {
                if(mp.count(a[L])){
                    add(mp[a[L]], a[L]);
                }
                mp[a[L]] = L;
                add(L, a[L]);
                ++ L;
            }
            int res = sum[Q[i].r] ^ sum[Q[i].l - 1];
            int res1 = ask(Q[i].r) ^ ask(Q[i].l - 1);
            res ^= res1;
            ans[Q[i].pos] = res;
        }
        for(int i = 1; i <= q; ++ i) {
            printf("%d
    ", ans[i]);
        }
        return 0;
    }
    View Code

      

    总结:

      省赛还是偏轻松愉快的2333,不过刚回来还是会犯一些低级错误,还有个trick因为太久没做题了,WA了一发才想起来。

      C(+1)的解法实现起来的时候,如果正向转移的话,同一层会自己影响自己,这是背包问题里比较常见的trick了,模拟赛中写的时候没有意识到这个问题。

      F(+5)暴露出的问题是读题不够仔细+对板子不过熟悉。首先是没有读到完整的题意,实际上做题的时候,如果有特别地说明区间跨度不超过100之类的,肯定是会针对这个条件来设计解法的。其次是题意读了个大概,然后发现和树套树的板子很像,就抛开题目直接上板子了。虽然实际上树套树确实可做,但因为数据量比较特殊,有更简单的解法,可以省下不少机时。

      G(+)虽然是一发过的,但是思路上绕了个大弯。我们队对各种数论筛法还是不够熟悉,上来有点念念不忘数论筛法,误以为这是个筛子题,想着怎么用筛子做。过了很久才发现这就是个结论题。

      H(+1)代码写的没问题,交之前顺手加了个记忆化,结果记忆化的部分没有用long long。

      J(+1)如果是我写的话,我可能也会犯这样的错误吧。动手写中期题的时候,还是要把思路想清楚再摸键盘。边写边想还是很容易漏掉一些点的。

      L(+1)数组开太小了。。

      还有就是我们讨论的时候容易想到什么就直接说,但我觉得这样讨论的效率其实是比较低的。我觉得应该先独立思考,有什么关键的进展,或者想到了什么解法,再和队友讨论。

  • 相关阅读:
    hdu4587 Two Nodes 求图中删除两个结点剩余的连通分量的数量
    洛谷3388 tarjan割点
    POJ1523 Tarjan求割点以及删除割点之后强连通分量的数量
    POJ1144 tarjan+网络中割点与割边的数量
    POJ1780 欧拉路+手写栈解决爆战问题
    Delphi 窗体函数GetForegroundWindow
    Delphi 窗体函数GetClassName
    Delphi 窗体函数GetDesktopWindow
    Delphi 窗体函数 GetTopWindow、GetNextWindow
    Delphi 调用惯例 register, pascal, cdecl, stdcall, safecall 介绍
  • 原文地址:https://www.cnblogs.com/Lubixiaosi-Zhaocao/p/14312764.html
Copyright © 2011-2022 走看看