2019牛客暑期多校训练营(第七场)

2019牛客暑期多校训练营(第七场)

题目链接

A.String

暴力\(dp\)即可。

Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 205;
int T;
char s[N];
int dp[N], pre[N];
bool check(int x, int y) {
    for(int d = 1; d <= y - x; d++) {
        for(int i = x; i <= y; i++) {
            int j = i + d;
            if(j > y) j = j - y + x - 1;
            if(s[i] != s[j]) {
                if(s[j] < s[i]) return 0;
                break ;
            }
        }
    }
    return 1;
}
int main() {
    ios::sync_with_stdio(false); cin.tie(0);
    cin >> T;
    while(T--) {
        cin >> s + 1;
        int n = strlen(s + 1);
        for(int i = 1; i <= n; i++) pre[i] = 0, dp[i] = n + 1;
        for(int i = 1; i <= n; i++) {
            for(int j = 0; j < i; j++) {
                if(dp[j] + 1 < dp[i] && check(j + 1, i)) {
                    dp[i] = dp[j] + 1;
                    pre[i] = j;
                }
            }
        }
        vector <int> d;
        d.push_back(n);
        int p = n;
        while(pre[p]) d.push_back(p = pre[p]);
        reverse(d.begin(), d.end());
        for(int i = 0; i < d.size(); i++) {
            int j = (i == 0 ? 0 : d[i - 1]);
            for(int k = j + 1; k <= d[i]; k++) {
                cout << s[k];
            }
            if(i != d.size() - 1) cout << ' ';
        }
        cout << '\n';
    }
    return 0;
}

B.Irreducible Polynomial

结论题,分情况讨论一下即可。

Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 10 + 5, INF = 0x3f3f3f3f, MOD = 1e9 + 7;
const ll inf = 1000000000000000010LL;
#define lson o<<1,l,m
#define rson o<<1|1,m+1,r
#define mid l + ((r-l)>>1)
 
int kase, n, a[MAXN];
int main() {
    ios::sync_with_stdio(false); cin.tie();
    cin >> kase;
    while (kase--) {
        cin >> n;
        for (int i = n; ~i; i--)cin >> a[i];
        if (n == 2) {
            ll x = (ll)a[1] * a[1] - 4LL * a[2] * a[0];
            if (x < 0)cout << "Yes\n";
            else cout << "No\n";
        }
        else if (n <= 1)cout << "Yes\n";
        else cout << "No\n";
    }
}

C.Governing sand

枚举最终最大高度,然后贪心砍掉费用最小的树即可。
首先会砍掉高度大于枚举值的树,之后根据数量在权值线段树中贪心找到答案。
注意不同种类的树的高度可能相同。

Code
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f3f3f3f3f
using namespace std;
typedef long long ll;
const int N = 1e5 + 5;
int n;
struct node{
    int h, c;
    ll p, sum;
    bool operator < (const node &A)const {
        return h < A.h;
    }
}a[N], b[N];
ll sum[N];
ll sz[200 << 2], sumv[200 << 2];
void build(int o, int l, int r) {
    sz[o] = sumv[o] = 0;
    if(l == r) return;
    int mid = (l + r) >> 1;
    build(o << 1, l, mid);
    build(o << 1|1, mid + 1, r);
}
void update(int o, int l, int r, int p, int k) {
    sz[o] += k; sumv[o] += 1ll * p * k;
    if(l == r) return ;
    int mid = (l + r) >> 1;
    if(p <= mid) update(o << 1, l, mid, p, k);
    else update(o << 1|1, mid + 1, r, p, k);
}
ll query(int o, int l, int r, ll k) {
    if(l == r) return sumv[o] / sz[o] * k;
    int mid = (l + r) >> 1;
    if(sz[o << 1] >= k) return query(o << 1, l, mid, k);
    return sumv[o << 1] + query(o << 1|1, mid + 1, r, k - sz[o << 1]);
}
int main() {
    ios::sync_with_stdio(false); cin.tie(0);
    while(cin >> n) {
        for(int i = 1; i <= n; i++) {
            int h, c, p; cin >> h >> c >> p;
            a[i] = {h, c, p, 1ll * c * p};
        }
        sort(a + 1, a + n + 1);
        int tot = 0;
        for(int i = 1; i <= n; i++) {
            if(a[i].h != a[i - 1].h) b[++tot] = a[i];
            else b[tot].sum += a[i].sum, b[tot].p += a[i].p;
        }
        sum[tot + 1] = 0;
        for(int i = tot; i >= 1; i--) sum[i] = sum[i + 1] + b[i].sum;
        build(1, 1, 200);
        int j = 1;
        ll ans = INF, cur = 0;
        for(int i = 1; i <= tot; i++) {
            ll tmp = sum[i + 1];
            ll p = b[i].p;
            if(p > cur) {
                ans = min(ans, tmp);
            } else {
                ans = min(ans, tmp + query(1, 1, 200, cur - p + 1));
            }
            cur += b[i].p;
            while(j <= n && a[j].h == b[i].h) {
                update(1, 1, 200, a[j].c, a[j].p);
                j++;
            }
        }
        cout << ans << '\n';
    }
    return 0;
}

D.Number

签到题。

Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 10 + 5, INF = 0x3f3f3f3f, MOD = 1e9 + 7;
const ll inf = 1000000000000000010LL;
#define lson o<<1,l,m
#define rson o<<1|1,m+1,r
#define mid l + ((r-l)>>1)
 
int n, p;
int get(int x) {
    int ans = 0;
    while (x) {
        x /= 10;
        ans++;
    }
    return ans;
}
int main() {
    ios::sync_with_stdio(false); cin.tie();
    cin >> n >> p;
    int m = get(p);
    if (n < m)cout << "T_T\n";
    else {
        cout << p;
        for (int i = 1; i <= n - m; i++)cout << "0";
        cout << '\n';
    }
    return 0;
}

E.Find the median

题目中生成的区间可能会很大,考虑离散化,但是这样就会影响原来区间的信息。
注意到有用的信息就是区间端点+覆盖次数。那么我们用线段树维护这些信息就行了。线段树中每个结点都维护的一个左闭右开的区间,另外有一些附加的信息,同时还保存每个叶子结点原来的大小(因为是离散化过后的)。
然后就解决了。详见代码吧:

Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 400005;
ll a1, b1, c1, m1;
ll a2, b2, c2, m2;
int x[N], y[N], b[N << 1];
int n, D;
void Hash() {
    for(int i = 1; i <= n; i++) {
        x[i]++; y[i]++;
        if(x[i] > y[i]) swap(x[i], y[i]);
        b[++D] = x[i], b[++D] = y[i] + 1;
    }
    sort(b + 1, b + D + 1);
    D = unique(b + 1, b + D + 1) - b - 1;
}
ll sumv[N << 3], cntv[N << 3], lazy[N << 3];
void build(int o, int l, int r) {
    sumv[o] = cntv[o] = lazy[o] = 0;
    if(l == r) return ;
    int mid = (l + r) >> 1;
    build(o << 1, l, mid);
    build(o << 1|1, mid + 1, r);
}
void push_down(int o, int l, int r) {
    if(lazy[o]) {
        int mid = (l + r) >> 1;
        lazy[o << 1] += lazy[o];
        cntv[o << 1] += lazy[o];
        sumv[o << 1] += lazy[o] * (b[mid + 1] - b[l]);
        lazy[o << 1|1] += lazy[o];
        cntv[o << 1|1] += lazy[o];
        sumv[o << 1|1] += lazy[o] * (b[r + 1] - b[mid + 1]);
        lazy[o] = 0;
    }
}
void push_up(int o) {
    sumv[o] = sumv[o << 1] + sumv[o << 1|1];
}
void update(int o, int l, int r, int L, int R) {
    if(L <= l && r <= R) {
        sumv[o] += b[r + 1] - b[l];
        cntv[o] += 1;
        lazy[o] += 1;
        return ;
    }
    push_down(o, l, r);
    int mid = (l + r) >> 1;
    if(L <= mid) update(o << 1, l, mid, L, R);
    if(R > mid) update(o << 1|1, mid + 1, r, L, R);
    push_up(o);
}
int query(int o, int l, int r, ll k) {
    if(l == r) return b[l] + ((k + cntv[o] - 1) / cntv[o]) - 1;
    push_down(o, l, r);
    int mid = (l + r) >> 1;
    if(sumv[o << 1] >= k) return query(o << 1, l, mid, k);
    else return query(o << 1|1, mid + 1, r, k - sumv[o << 1]);
}
int main() {
    ios::sync_with_stdio(false); cin.tie(0);
    cin >> n;
    cin >> x[1] >> x[2] >> a1 >> b1 >> c1 >> m1;
    cin >> y[1] >> y[2] >> a2 >> b2 >> c2 >> m2;
    for(int i = 3; i <= n; i++) {
        x[i] = (a1 * x[i - 1] % m1 + b1 * x[i - 2] % m1 + c1) % m1;
        y[i] = (a2 * y[i - 1] % m2 + b2 * y[i - 2] % m2 + c2) % m2;
    }
    Hash();
    build(1, 1, D);
    ll sum = 0;
    for(int i = 1; i <= n; i++) {
        sum += y[i] - x[i] + 1;
        int L = lower_bound(b + 1, b + D + 1, x[i]) - b;
        int R = lower_bound(b + 1, b + D + 1, y[i] + 1) - b - 1;
        update(1, 1, D, L, R);
        cout << query(1, 1, D, (sum + 1) / 2) << '\n';
    }
    return 0;
}

H.Pair

数位\(dp\),加上限制:一个是数字本身的限制,另一个是是否满足题目条件的限制。
对于\(xor\),要求\(t\leq x\) \(xor\) \(y\),只要二进制高位一位满足,其余就可以随便取了;同理对于\(x\) \(and\) \(y \leq t\),只要一位满足小于,后面的也可以随便取。
注意一下\(x,y\)\(0\)的情况。

Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 32;
int T;
ll dp[N][2][2][2][2];
int a, b, c;
int na[N], nb[N], nc[N];
ll dfs(int pos, int o1, int o2, int lim1, int lim2) {
    if(pos < 0) return 1;
    ll &t = dp[pos][o1][o2][lim1][lim2];
    if(t != -1) return t;
    int up1 = (o1 ? na[pos] : 1);
    int up2 = (o2 ? nb[pos] : 1);
    ll res = 0;
    for(int i = 0; i <= up1; i++) {
        for(int j = 0; j <= up2; j++) {
            int p = i & j;
            int q = i ^ j;
            if(lim1 && q < nc[pos]) continue;
            if(lim2 && p > nc[pos]) continue;
            res += dfs(pos - 1, o1 && (i == up1), o2 && (j == up2), lim1 && (q == nc[pos]), lim2 && (p == nc[pos]));
        }
    }
    return t = res;
}
int main() {
    ios::sync_with_stdio(false); cin.tie(0);
    cin >> T;
    while(T--) {
        memset(dp, -1, sizeof(dp));
        cin >> a >> b >> c;
        for(int i = 31; i >= 0; i--) {
            na[i] = (a >> i & 1) ? 1 : 0;
            nb[i] = (b >> i & 1) ? 1 : 0;
            nc[i] = (c >> i & 1) ? 1 : 0;
        }
        ll ans = dfs(31, 1, 1, 1, 1);
        ans -= max(0, a - c + 1);
        ans -= max(0, b - c + 1);
        cout << (1ll * a * b) - ans << '\n';
    }
    return 0;
}

J.A+B problem

签到题。

K.Function

题意:
\(\sum_{i=1}^nf(i)\),其中,\(f(i)\)为:

\[\left\{ \begin{aligned} &3e+1,&i= p^e \ and\ p\%4=1\\ &1,&else \end{aligned} \right. \]

思路:
考虑\(min25\)筛求解。
我们不管1,2的存在,最后加上其贡献即可。
首先求出\(g_1,g_2\),分别质数表示\(\% 4\)为1和3的情况总数,然后一个一个来筛。筛的时候要同时考虑当前质数模4的情况以及对\(g_1,g_2\)的影响,它们都会减去某个值。
然后就直接求和就行了,基本都是套用板子。
最主要的还是求\(g\)的思路。

Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e6 + 5;
int T;
ll n;
ll sum1[N], sum2[N], prime[N];
ll w[N], ind1[N], ind2[N];
ll g1[N], g2[N];
bool chk[N];
int tot, cnt;
void pre(int n) { //  \sqrt
    chk[1] = 1;
    for(int i = 1; i <= n; i++) {
        if(!chk[i]) {
            prime[++tot] = i;
            sum1[tot] = sum1[tot - 1];
            sum2[tot] = sum2[tot - 1];
            if(i % 4 == 1) ++sum1[tot];
            if(i % 4 == 3) ++sum2[tot];
        }
        for(int j = 1; j <= tot && prime[j] * i <= n; j++) {
            chk[i * prime[j]] = 1;
            if(i % prime[j] == 0) break;
        }
    }
}
void calc_g() {
    int z = sqrt(n);
    for(ll i = 1, j; i <= n; i = j + 1) {
        j = n / (n / i);
        w[++cnt] = n / i;
        g1[cnt] = w[cnt] / 4 + (w[cnt] % 4 >= 1) - 1;
        g2[cnt] = w[cnt] / 4 + (w[cnt] % 4 >= 3);
        if(n / i <= z) ind1[n / i] = cnt;
        else ind2[n / (n / i)] = cnt;
    }
    for(int i = 1; i <= tot; i++) {
        for(int j = 1; j <= cnt && prime[i] * prime[i] <= w[j]; j++) {
            ll tmp = w[j] / prime[i], k;
            if(tmp <= z) k = ind1[tmp]; else k = ind2[n / tmp];
            if(prime[i] % 4 == 1) {
                g1[j] -= (g1[k] - sum1[i - 1]);
                g2[j] -= (g2[k] - sum2[i - 1]);
            } else if(prime[i] % 4 == 3) {
                g1[j] -= (g2[k] - sum2[i - 1]);
                g2[j] -= (g1[k] - sum1[i - 1]);
            }
        }
    }
}
int f(int p, int q) {
    if(p % 4 == 1) return 3 * q + 1;
    return 1;
}
ll S(ll x, int y) { // 2~x >= P_y
    if(x <= 1 || prime[y] > x) return 0;
    ll z = sqrt(n);
    ll k = x <= z ? ind1[x] : ind2[n / x];
    ll ans = 4 * g1[k] - 4 * sum1[y - 1] + g2[k] - sum2[y - 1];
    if(y == 1) ans++;
    for(int i = y; i <= tot && prime[i] * prime[i] <= x ; i++) {
        ll pe = prime[i], pe2 = prime[i] * prime[i];
        for(int e = 1; pe2 <= x; ++e, pe = pe2, pe2 *= prime[i]) {
            ans += f(prime[i], e) * S(x / pe, i + 1) + f(prime[i], e + 1);
        }
    }
    return ans;
}
int main() {
    freopen("input.in", "r", stdin);
    cin >> T;
    while(T--) {
        memset(chk, 0, sizeof(chk)); tot = cnt = 0;
        cin >> n;
        int tmp = sqrt(n);
        pre(tmp);
        calc_g();
        cout << S(n, 1) + 1 << '\n';
    }
    return 0;
}

posted @ 2019-08-10 20:02  heyuhhh  阅读(257)  评论(0编辑  收藏  举报