牛客练习赛51

传送门

A - abc

题意:
给出字符串\(s\),要求统计\(s\)中子序列为\("abc"\)的个数。

思路:
分别统计一下前缀\(a\)和后缀\(c\)的个数即可。

Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MOD = 1e9 + 7;
const int N = 1e5 + 5;
ll qp(ll a, ll b) {
    ll ans = 1;
    while(b) {
        if(b & 1) ans = ans * a % MOD;
        a = a * a % MOD;
        b >>= 1;
    }
    return ans;
}
char s[N];
int prea[N], sufc[N];

int main() {
    ios::sync_with_stdio(false); cin.tie(0);
    cin >> s + 1;
    int n = strlen(s + 1);
    for(int i = 1; i <= n; i++) {
        prea[i] = prea[i - 1] + (s[i] == 'a');
    }
    for(int i = n; i >= 1; i--) {
        sufc[i] = sufc[i + 1] + (s[i] == 'c');
    }
    ll ans = 0;
    for(int i = 1; i <= n; i++) {
        if(s[i] == 'b') ans += 1ll * prea[i] * sufc[i];
    }
    cout << ans;
    return 0;
}

B - 子串查询

题意:
给出一个字符串\(s\),之后有多个询问,每个询问会有一个串\(t\),回答\(t\)是否为\(s\)的子序列。

思路:
预处理下一位,模拟一下即可。

Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MOD = 1e9 + 7;
const int N = 1e5 + 5;
ll qp(ll a, ll b) {
    ll ans = 1;
    while(b) {
        if(b & 1) ans = ans * a % MOD;
        a = a * a % MOD;
        b >>= 1;
    }
    return ans;
}
int n, q;
char s[N];
char t[55];
int nxt[N][26], Next[26];
int main() {
    ios::sync_with_stdio(false); cin.tie(0);
    cin >> n >> q;
    cin >> s + 1;
    for(int i = n; i >= 0; i--) {
        for(int j = 0; j < 26; j++) {
            nxt[i][j] = Next[j];
        }
        Next[s[i] - 'a'] = i;
    }
    for(int i = 1; i <= q; i++) {
        cin >> t + 1;
        int now = 0, len = strlen(t + 1), f = 1;
        for(int j = 1; j <= len; j++) {
            now = nxt[now][t[j] - 'a'];
            if(now == 0) {
                f = 0; break;
            }
        }
        if(now == 0) cout << "NO" << '\n';
        else cout << "YES" << '\n';
    }
    return 0;
}

C - 勾股定理

数学题,百度一下...

Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MOD = 1e9 + 7;
const int N = 1e5 + 5;
ll qp(ll a, ll b) {
    ll ans = 1;
    while(b) {
        if(b & 1) ans = ans * a % MOD;
        a = a * a % MOD;
        b >>= 1;
    }
    return ans;
}
ll n;
int main() {
    ios::sync_with_stdio(false); cin.tie(0);
    cin >> n;
    if(n < 3) {
        cout << -1; return 0;
    }
    if(n & 1) {
        ll b = (n * n - 1) / 2, c = (n * n + 1) / 2;
        cout << b << ' ' << c << '\n';
    } else {
        ll tmp = n * n / 2;
        if(tmp % 2 != 0) {
            cout << -1;
        } else {
            ll b = (n * n / 2 - 2) / 2, c = (n * n / 2 + 2) / 2;
            cout << b << ' ' << c;
        }
    }
    return 0;
}

D - 羊吃草

题意:
现在有一个长度为\(400\)的数轴,并且存在\(n\)头羊,每头羊会吃范围在\([a_i,b_i]\)上面的草,每头羊只能吃一个位置上面的草并且一个位置只能被一只羊吃。
现在有多个询问,对于每个询问会有一个区间\([l,r]\),询问区间\([l,r]\)上最多有多少只羊在这上面吃草。

思路:
显然可以直接二分图最大匹配来搞。
但有一种很好写的方法:

  • 对于每次的询问,我们每个位置逐一考虑。
  • 当我们考虑到位置\(i\)时,我们会选择一头羊来进行匹配,这时贪心选择即可,前提是羊吃草的区间包含\(i\)
  • 具体来说,就是每个羊尽量在最后时刻才吃草,那么我们在前面的时刻都可以选择。
  • \(i\)位置考虑完了过后,我们要把最晚位置为\(i\)且还没匹配的羊删去,这是显然的。

这可以用\(multiset\)来维护,\(erase\)某个值时能将那个值全部删除。

Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 405;

multiset <int> s;

int n, q;
int a[N], b[N];

int main() {
    ios::sync_with_stdio(false); cin.tie(0);
    cin >> n >> q;
    for(int i = 1; i <= n; i++) cin >> a[i];
    for(int i = 1; i <= n; i++) cin >> b[i];
    while(q--) {
        s.clear();
        int l, r; cin >> l >> r;
        int ans = 0;
        for(int i = 1; i <= n; i++) {
            if(a[i] < l && b[i] >= l) s.insert(b[i]);
        }
        for(int i = l; i <= r; i++) {
            for(int j = 1; j <= n; j++) if(i == a[j]) s.insert(b[j]);
            if(s.size()) ++ans, s.erase(s.begin());
            s.erase(i);
        }
        cout << ans << '\n';
    }
    return 0;
}

E - 数列

题意:
现在有长度为\(n\)的空位,现在我们要往中间填入一些数。
若存在对于\(i>1,a[i]=a[i-1]+1\),那么答案会加一。
先问怎么分配,能使答案最大并且总和不超过\(m\)

思路:

  • 注意到肯定是\(1,2,\cdots,1,\cdots\)这样来是最优的。
  • 我们将连续递增的几个数统称为“块”,显然,假设最终有\(x\)块,那么答案就为\(n-x\)
  • 并且可以发现:块越多,总和越小;否则总和越大,也就是说块的数量是具有单调性的,那么我们就可以二分块的个数。
  • 怎么分配?
  • 注意到若存在:\(1,2,\cdots,x,1,2,\cdots,y\)并满足\(y>x+1\)这种,那么我们将\(y\)放到\(x\)后面(变为\(x+1\)了)是肯定更优的。
  • 所以就平均分配就行了~

简洁来说,就是二分+均分即可,这些性质也比较显然。

Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 5;

int n, m;

bool chk(int x) {
    int p = n / x;
    int q = n % x;
    ll now = 1ll * q * (p + 1) * (p + 2) / 2;
    now += 1ll * (x - q) * (p + 1) * p / 2;
    return now <= m;
}

int main() {
    ios::sync_with_stdio(false); cin.tie(0);
    cin >> n >> m;
    int l = 1, r = n + 1, mid;
    while(l < r) {
        mid = (l + r) >> 1;
        if(chk(mid)) r = mid;
        else l = mid + 1;
    }
    int p = n / r, q = n % r;
    for(int i = 1; i <= q; i++) {
        for(int j = 1; j <= p + 1; j++) {
            cout << j << ' ';
        }
    }
    for(int i = 1; i <= r - q; i++) {
        for(int j = 1; j <= p; j++) {
            cout << j << ' ';
        }
    }
    return 0;
}

posted @ 2019-09-10 20:54  heyuhhh  阅读(192)  评论(0编辑  收藏  举报