• 2019牛客多校第三场题解


    2019牛客多校第三场题解

    题目链接

    B.Crazy Binary String

    子序列维护前缀和即可,子串答案(2*min(0,1))的个数。

    Code
    #include<bits/stdc++.h>
    typedef long long ll;
    const int MAXN = 1e5 + 5, MAXM = 1e5 + 5, INF = 0x3f3f3f3f, MOD = 998244353;
    const ll INFL = 0x3f3f3f3f3f3f3f3f;
    using namespace std;
    #define lson o<<1,l,m
    #define rson o<<1|1,m+1,r
    #define mid l + ((r-l)>>1)
    #define pb push_back
    #define random(a,b) ((a)+rand()%((b)-(a)+1))
    typedef double db;
    int n, cnt[2], mp[MAXN << 1], delta = 100000;
    char t[MAXN];
    int main() {
        ios::sync_with_stdio(false); cin.tie(0);
        cin >> n;
        cin >> (t + 1);
        int sum = 0, ans = 0;
        for (int i = 1; i <= n; i++) {
            cnt[t[i] - '0']++;
            if (t[i] == '1')sum++;
            else sum--;
            if (sum == 0)ans = max(ans, i);
            if (mp[sum + delta]) {
                ans = max(ans, i - mp[sum + delta]);
            }
            else mp[sum + delta] = i;
        }
        cout << ans << ' ' << min(cnt[0], cnt[1]) * 2 << '
    ';
        return 0;
    }
    

    D.Big Integer

    易得到(A(n)=frac{10^n-1}{9}),所以题目就是要求(A(n)=frac{10^n-1}{9}=0(modp))
    考虑(p!=3)时,有((10^n-1)*inv(9)=0(mod p)),因(inv(9)!=0),故式子可以化为(10^n=1(modp))

    再考虑(p!=2,p!=5)的情况,那么就有(gcd(10,p)=1),所以(10^{varphi(p)}=1(modp)),因为(10^0=1(modp)),故可以知道一个循环节为(varphi(p)=p-1)。现在我们要求最小循环节(d),那么肯定满足(d|(p-1)),这里我们直接暴力枚举来求就行了。

    接下来就是求对于所有的(1leq ileq n,1leq jleq m),满足(d|i^j)的个数。
    (d)进行分解为:(p_1^{k_1}p_2^{k_2}...p_t^{k_t}),那么要满足上面的条件就有(g=p_1^{lceil frac{k_1}{j} ceil}p_2^{lceil frac{k_2}{j} ceil}...p_t^{lceil frac{k_t}{j} ceil}|i),此时(i)的个数为(frac{n}{g})。之后枚举(j)就行了,这里我们只用枚举到(30),再大一点值都是一样的了。
    如果(p=2||p=5)时,答案显然为(0)

    现在考虑(p=3)的情况,对于一个数来说,如果它为(3)的倍数,那么其每个位置上面的数字之和就为(3)的倍数。对于这个题来说就是求(i^j=0(mod 3))的所有(i,j)个数。直接算就行了。

    详细见代码吧,这个题也可以不把(9)约去,直接枚举(varphi(9p))的约数。但是因为模数超过了int,可能会爆long long。

    代码如下:

    Code
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 1e5 + 5, INF = 1e9;
    int T;
    int n, m, p;
    ll qp(ll a, ll b, int P) {
        ll ans = 1;
        while(b) {
            if(b & 1) ans = ans * a % P;
            a = a * a % P;
            b >>= 1;
        }
        return ans;
    }
    int pri[N], cnt[N], k;
    void work(int x, int *a, int *b) {
        k = 0;
        for(int i = 2; 1ll * i * i <= x; i++) {
            if(x % i == 0) {
                a[++k] = i; int c = 0;
                while(x % i == 0) c++, x /= i;
                b[k] = c;
            }
        }
        if(x > 1) {
            a[++k] = x; b[k] = 1;
        }
    }
    int solve(int j) {
        int x = 1;
        for(int i = 1; i <= k; i++) {
            int t = (cnt[i] + j - 1) / j;
            while(t--) x *= pri[i];
        }
        return n / x;
    }
    int main() {
        ios::sync_with_stdio(false); cin.tie(0);
        cin >> T;
        while(T--) {
            cin >> p >> n >> m;
            if(p == 2 || p == 5) {
                cout << 0 << '
    ';
                continue ;
            }
            if(p == 3) {
                cout << 1ll * m * (n / 3) << '
    ';
                continue;
            }
            int phi = p - 1, d = INF;
            for(int i = 1; 1LL * i * i <= phi; i++) {
                if(phi % i == 0) {
                    if(qp(10, i, p) == 1) d = min(d, i);
                    if(qp(10, phi / i, p) == 1) d = min(d, phi / i);
                }
            }
            work(d, pri, cnt);
            ll ans = 0;
            for(int j = 1; j <= min(30, m); j++) ans += solve(j);
            if(m > 30) ans += 1ll * (m - 30) * solve(30);
            cout << ans << '
    ';
        }
        return 0;
    }
    
    
    ####
    F.Planting Trees
    一开始写的$O(n^3logn)$的二维st表,没卡过去= = 然后其实直接单调队列就行了,枚举矩形的上下边界,然后枚举右边界,维护最小可行左边界。因为左边界是单调不减的,复杂度就是$O(n^3)$。实现的话用个指针来记录就行了。 代码如下:
    Code
        #include <bits/stdc++.h>
        using namespace std;
        typedef long long ll;
        const int N = 505, INF = 1e9;
        int T;
        int n, m;
        int a[N][N];
        int cmx[N], cmn[N];
        int q1[N], q2[N];
        int l1, r1, l2, r2;
        int main() {
            ios::sync_with_stdio(false); cin.tie(0);
            cin >> T;
            while(T--) {
                cin >> n >> m;
                int ans = 0;
                for(int i = 1; i <= n; i++) for(int j = 1; j <= n; j++) cin >> a[i][j];
                for(int i = 1; i <= n; i++) {
                    for(int j = 1; j <= n; j++) cmx[j] = 0, cmn[j] = INF;
                    for(int j = i; j <= n; j++) {
                        for(int k = 1; k <= n; k++) cmx[k] = max(cmx[k], a[j][k]), cmn[k] = min(cmn[k], a[j][k]);
                        l1 = l2 = 1; r1 = r2 = 0;
                        int p = 1;
                        for(int k = 1; k <= n; k++) {
                            while(l1 <= r1 && cmx[q1[r1]] <= cmx[k]) r1--;
                            q1[++r1] = k;
                            while(l2 <= r2 && cmn[q2[r2]] >= cmn[k]) r2--;
                            q2[++r2] = k;
                            while(cmx[q1[l1]] - cmn[q2[l2]] > m) {
                                p++;
                                if(l1 <= r1 && q1[l1] < p) l1++;
                                if(l2 <= r2 && q2[l2] < p) l2++;
                                if(p > k) break;
                            }
                            ans = max(ans, (j - i + 1) * (k - p + 1));
                        }
                    }
                }
                cout << ans << '
    ';
            }
            return 0;
        }
    

    G.Removing Stones

    可以发现必胜条件为:对于一段区间来说,(sum_r-sum_{l-1}>=2*mx)
    一个区间的最大值可以将区间分为两个部分,所以就可以考虑分治,对于最大值的位置分成的两个区间,枚举范围小的那个区间,在另一个区间里面二分就行了(枚举范围大的复杂度可能退化为O(n^2)而不是(O(nlogn)))。
    复杂度(O(nlognlogn)),求最大位置时还需要个st表,不然会T。

    Code
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 300005;
    int T;
    int n;
    int a[N];
    ll sum[N];
    ll ans;
    int f[N][19], pos[N][19];
    int lg[N];
    void init() {
        for(int i = 1; i <= n; i++) f[i][0] = a[i], pos[i][0] = i;
        for(int j = 1; j <= 18; j++) {
            for(int i = 1; i + (1 << j) - 1 <= n; i++) {
                if(f[i][j - 1] > f[i + (1 << (j - 1))][j - 1]) {
                    pos[i][j] = pos[i][j - 1];
                } else pos[i][j] = pos[i + (1 << (j - 1))][j - 1];
                f[i][j] = max(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]) ;
            }
        }
    }
    int query(int l, int r) {
        int k = lg[r - l + 1];
        if(f[l][k] > f[r - (1 << k) + 1][k]) return pos[l][k];
        return pos[r - (1 << k) + 1][k];
    }
    void solve(int l, int r) {
        if(r - l <= 0) return;
        if(r - l == 1) {
            if(a[l] == a[r]) ans++;
            return;
        }
        int k = query(l, r);
        int mx = a[k];
        if(k - l < r - k) {
            for(int L = l; L <= k; L++) {
                int LL = k, RR = r + 1, mid;
                while(LL < RR) {
                    mid = (LL + RR) >> 1;
                    if(sum[mid] - sum[L - 1] - mx >= mx) RR = mid;
                    else LL = mid + 1;
                }
                ans += (r - LL + 1);
            }
        } else {
            for(int R = k; R <= r; R++) {
                int LL = l ,RR = k + 1, mid;
                while(LL < RR) {
                    mid = (LL + RR) >> 1;
                    if(sum[R] - sum[mid - 1] - mx >= mx) LL = mid + 1;
                    else RR = mid;
                }
                ans += (LL - l);
            }
        }
        solve(l, k - 1); solve(k + 1, r);
    }
    int main() {
        ios::sync_with_stdio(false); cin.tie(0);
        for(int i = 2; i < N; i++) lg[i] = lg[i >> 1] + 1;
        cin >> T;
        while(T--) {
            cin >> n; ans = 0;
            for(int i = 1; i <= n; i++) cin >> a[i], sum[i] = sum[i - 1] + a[i];
            init();
            solve(1, n);
            cout << ans << '
    ';
        }
        return 0;
    }
    
    

    H.Magic Line

    按x,y进行排序,之后考虑稍微倾斜直线就行了。

    Code
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 1005, INF = 1e8;
    int T;
    struct point{
        int x, y;
        bool operator < (const point &A)const {
            if(x == A.x) return y < A.y;
            return x < A.x;
        }
    }a[N];
    int n;
    int main() {
        ios::sync_with_stdio(false); cin.tie(0);
        cin >> T;
        while(T--) {
            cin >> n;
            for(int i = 1; i <= n; i++) cin >> a[i].x >> a[i].y;
            sort(a + 1, a + n + 1);
            if(a[n / 2].x == a[n / 2 + 1].x) {
                cout << a[n / 2].x - 1 << ' ' << a[n / 2].y + INF << ' ' << a[n / 2 + 1].x + 1 << ' ' << a[n / 2 + 1].y - INF << '
    ';
            } else {
                cout << a[n / 2].x << ' ' << -INF << ' ' << a[n / 2 + 1].x << ' ' << INF << '
    ';
            }
        }
        return 0;
    }
    
    

    I.Median

    有个结论就是(a_i)一定等于影响它的中位数三者之一。
    证明的话可以手推一下,最后会发现(a_i)要么都大于等于这三个数,要么都小于等于,显然取等于是可行的。
    之后就进行(dp),对于当前这一位(i),通过(i-2,i-1)转移过来,设(dp[i][j][k])表示第(i)位用与之相关的第(j+1)大中位数,(i-1)位用与之相关的第(k+1)大中位数,最后从(dp[i-1][k][l])转移过来。
    (v[i][j])储存的就是影响第(i)个数的第(j+1)大中位数。
    转移的时候记录一下前驱就行了。
    合法性判断的时候注意一下一个是前面这个状态合法,另一个是填当前这个数的话满足中位数等于(b_{i-1})(代码里面的下标是(i-1),实际上是(i-2))
    代码如下:

    Code
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 1e5 + 5;
    int T;
    int b[N], a[N];
    int dp[N][3][3], v[N][3], pre[N][3][3];
    int n;
    int med(int x, int y, int z) {
        int tmp[3];
        tmp[0] = x, tmp[1] = y, tmp[2] = z;
        sort(tmp, tmp + 3);
        return tmp[1];
    }
    int main() {
        ios::sync_with_stdio(false); cin.tie(0);
        cin >> T;
        while(T--) {
            for(int i = 1; i <= n; i++)
                for(int j = 0; j < 3; j++)
                    for(int k = 0; k < 3; k++)
                        dp[i][j][k] = pre[i][j][k] = 0;
            cin >> n;
            for(int i = 2; i < n; i++) cin >> b[i];
            b[0] = b[1] = b[2];
            b[n + 1] = b[n] = b[n - 1];
            for(int i = 1; i <= n; i++) {
                for(int j = 0; j < 3; j++) {
                    v[i][j] = b[i + j - 1];
                }
                sort(v[i], v[i] + 3);
            }
            for(int i = 1; i <= 2; i++)
                for(int j = 0; j < 3; j++)
                    for(int k = 0; k < 3; k++)
                        dp[i][j][k] = 1;
            for(int i = 3; i <= n; i++) {
                for(int j = 0; j < 3; j++) {
                    for(int k = 0; k < 3; k++) {
                        for(int l = 0; l < 3; l++) {
                            if(!dp[i - 1][k][l]) continue;
                            if(med(v[i - 2][l], v[i - 1][k], v[i][j]) != b[i - 1]) continue;
                            dp[i][j][k] = 1;
                            pre[i][j][k] = l;
                        }
                    }
                }
            }
            int x = -1, y = -1;
            for(int i = 0; i < 3; i++) for(int j = 0; j < 3; j++) {
                if(dp[n][i][j]) {
                    x = i, y = j;
                    break ;
                }
            }
            if(x == -1 || y == -1) {
                cout << -1 << '
    ';
                continue ;
            }
            for(int i = n; i >= 1; i--) {
                a[i] = v[i][x];
                x = pre[i][x][y];
                swap(x, y);
            }
            for(int i = 1; i <= n; i++) cout << a[i] << ' ' ;
            cout << '
    ';
        }
        return 0;
    }
    
    

    J.LRU management

    直接模拟,细节有点多。。。

    Code
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 500005, M = 15;
    int T;
    char s[M];
    int st, ed, sz;
    int q, m;
    int L[N], R[N], data[N];
    int trie[N * 10], pos[N * 10], ch[N * 10][10], tot;
    void trieInsert(int it, char *s){
        assert(strlen(s) <= 10);
        int p = 0;
        for (int i = 0; s[i]; i++){
            int &t = ch[p][s[i] - '0'];
            if (!t) t = ++tot; p = t;
        }
        pos[trie[it] = p] = it;
    }
    
    int trieFind(char *s){
        int p = 0;
        for (int i = 0; s[i]; i++){
            p = ch[p][s[i] - '0'];
            if(!p) return 0;
        }
        return pos[p];
    }
    void Erase(int x){
        int pre = L[x], succ = R[x];
        if (pre) R[pre] = succ;
        if (succ) L[succ] = pre;
        data[x] = L[x] = R[x] = 0;
        if (x == st) st = succ;
        if (x == ed) ed = pre;
        --sz;
    }
    
    void Insert(int x, int v){
        data[x] = v;
        if (!st) st = ed = x;
        else L[R[ed] = x] = ed, ed = x;
        ++sz;
    }
    int main() {
        ios::sync_with_stdio(false); cin.tie(0);
        cin >> T;
        while(T--) {
            st = ed = sz = 0;
            pos[tot = 0] = 0;
            cin >> q >> m;
            int num = 0;
            while(q--) {
                int op, v;
                cin >> op >> s + 1 >> v;
                int it = trieFind(s + 1);
                if(op == 1) {
                    if(!it || (v == 1 && !R[it]) || (v == -1 && !L[it])) cout << "Invalid" << '
    ';
                    else {
                        if(v == -1) it = L[it];
                        if(v == 1) it = R[it];
                        cout << data[it] << '
    ';
                    }
                } else {
                    if(it) {
                        v = data[it];
                        Erase(it);
                    }
                    if(sz == m) pos[trie[st]] = 0, Erase(st);
                    Insert(++num, v);
                    trieInsert(num, s + 1);
                    cout << v << '
    ';
                }
            }
            for(int i = 0; i <= tot; i++) {
                for(int j = 0; j < 10; j++) ch[i][j] = 0;
                pos[i] = trie[i] = 0;
            }
            while(st) Erase(st);
        }
        return 0;
    }
    
    
  • 相关阅读:
    数据后台查询分页条件查询数据
    避免js缓存
    jquery点击按钮
    网页内容打印
    数据表的导出
    C#实现字符串按多个字符采用Split方法分割
    JQuery
    AJAX
    JSON
    BOM
  • 原文地址:https://www.cnblogs.com/heyuhhh/p/11260357.html
Copyright © 2020-2023  润新知