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 << '\n';
    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)\),这里我们直接暴力枚举来求就行了。

接下来就是求对于所有的\(1\leq i\leq n,1\leq j\leq 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}\rceil}p_2^{\lceil \frac{k_2}{j}\rceil}...p_t^{\lceil \frac{k_t}{j}\rceil}|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 << '\n';
            continue ;
        }
        if(p == 3) {
            cout << 1ll * m * (n / 3) << '\n';
            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 << '\n';
    }
    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 << '\n';
        }
        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 << '\n';
    }
    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 << '\n';
        } else {
            cout << a[n / 2].x << ' ' << -INF << ' ' << a[n / 2 + 1].x << ' ' << INF << '\n';
        }
    }
    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 << '\n';
            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 << '\n';
    }
    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" << '\n';
                else {
                    if(v == -1) it = L[it];
                    if(v == 1) it = R[it];
                    cout << data[it] << '\n';
                }
            } 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 << '\n';
            }
        }
        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;
}

posted @ 2019-07-28 18:36  heyuhhh  阅读(418)  评论(0编辑  收藏  举报