暑假N天乐【比赛篇】 —— 2019牛客暑期多校训练营(第七场)

以下题解包括:\(A \ \ \ B \ \ \ C \ \ \ D \ \ \ E \ \ \ J\)

另:\(H【Pair】数位dp \ 待补\) 毕竟我还不会

比赛地址: https://ac.nowcoder.com/acm/contest/887#question

【A】 String 最小表示法

给定一个字符串,要把它分成最少段的最小表示法串,输出分段之后的字符串。

从前往后依次枚举,不断缩短长度直到当前前缀为最小表示法即可,然后输出再从当前末尾开始判断。

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <bitset>
#include <cctype>
#include <cstdio>
#include <vector>
#include <string>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iomanip>
#include <numeric>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double PI = acos(-1.0);
const double eps = 1e-6;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;

char s[205];
string temp;
int l;

int get_min() {
    int i = 0, j = 1;
    int k = 0;
    while(i < l && j < l && k < l) {
        int t = temp[(i+k)%l] - temp[(j+k)%l];
        if(t == 0) {
            k ++;
        }
        else {
            if(t > 0) {
                i = i + k + 1;
            }
            else {
                j = j + k + 1;
            }
            k = 0;
            if(i == j) {
                j ++;
            }
        }
    }
    return i < j ? i : j;
}

int main() {
    int t;
    scanf("%d", &t);
    while(t--) {
        scanf("%s", s);
        int n = strlen(s);
        int pos = 0;
        while(1) {
            temp = "";
            for(int i = pos; i < n; i++) {
                temp = temp + s[i];
            }
            l = temp.length();
            while(get_min() != 0) {
                l--;
            }
            pos = pos + l;
            for(int i = 0; i < l; i ++) {
                cout << temp[i];
            }
            if(pos == n) {
                printf("\n");
                break;
            }
            else {
                printf(" ");
            }
        }
    }
    return 0;
}

【B】 Irreducible Polynomial 数学

给定一个一元 \(n\) 次方程,问能不能把它分解成连乘的形式。

又是个定理题,次数大于等于 3 一定不行,等于 1 一定可以,等于二的时候需要判断 \(b^2 - 4ac < 0\) 是否成立,若成立,则可以。

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <bitset>
#include <cctype>
#include <cstdio>
#include <vector>
#include <string>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iomanip>
#include <numeric>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double PI = acos(-1.0);
const double eps = 1e-6;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;
 
int a[30];
 
int main() {0;
}
    int t;
    scanf("%d", &t);
    while(t--) {
        int n;
        scanf("%d", &n);
        for(int i = n; i >= 0; i--) {
            scanf("%d", &a[i]);
        }
        if(n >= 3) {
            printf("No\n");
            continue;
        }
        if(n == 1) {
            printf("Yes\n");
            continue;
        }
        if(a[1]*a[1]-4*a[0]*a[2] < 0) {
            printf("Yes\n");
        }
        else {
            printf("No\n");
        }
    }
    return 0;
}

【C】 Governing sand 贪心

我也不知道为什么比赛时候没看到 "c <= 200" 这个条件,然后队友敲了个权值线段树维护,虽然过了也是浪费了挺多时间的,还把学弟坑了...以下是赛后写的正解(大概)。

给定 \(n\) 种树,每种都有它的高度 \(h(\leq1e^9)\),砍倒的费用 \(c(\leq 200)\)以及数量 \(p(\leq 1e^9)\)。现在需要让最高的树的数量占比超过 50%,问最小花费是多少。

其实就是各种排序贪心一下就好了。

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <bitset>
#include <cctype>
#include <cstdio>
#include <vector>
#include <string>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iomanip>
#include <numeric>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double PI = acos(-1.0);
const double eps = 1e-6;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;

const int maxn = 1e5+5;

int n;
ll sum[maxn];
ll cost[205];
ll numh[maxn];
vector<ll> v;
struct node {
    ll h, c, p;
    bool operator < (const node &q) const {
        return h < q.h;
    }
}a[maxn];

int getid(ll x) {
    return lower_bound(v.begin(), v.end(), x) - v.begin() + 1;
}

int main() {
    while(~scanf("%d", &n)) {
        v.clear();
        for(int i = 1; i <= n; i++) {
            scanf("%lld%lld%lld", &a[i].h, &a[i].c, &a[i].p);
            v.push_back(a[i].h);
        }
        sort(a+1, a+1+n);
        sort(v.begin(), v.end());
        v.erase(unique(v.begin(), v.end()), v.end());
        memset(sum, 0, sizeof(sum));
        memset(numh, 0, sizeof(numh));
        for(int i = 1; i <= n; i++) {
            int id = getid(a[i].h);
            sum[id] += sum[id-1] + 1ll*a[i].p*a[i].c;
            numh[id] += numh[id-1] + 1ll*a[i].p;
        }
        ll ans = 1e18;
        memset(cost, 0, sizeof(cost));
        int new_n = v.size();
        for(int i = 1; i <= n; i++) {
            int id = getid(a[i].h);
            ll temp = sum[new_n] - sum[id]; // 删掉所有比它高的树的代价
            ll num = numh[id] - numh[id-1]; // 这种高度的树的数量
            ll num_small = numh[id-1];      // 比他矮的树的数量
            // cout << temp << "  " << num << "  " << num_small << endl;
            if(num > num_small) {           // 不用砍了
                ans = min(ans, temp);   
                cost[a[i].c] += 1ll*a[i].p;
                continue;
            }
            ll more = num_small - num + 1;  // 需要砍掉多少
            for(int j = 1; j <= 200; j++) {
                if(cost[j] == 0) {
                    continue;
                }
                if(more - cost[j] >= 0) {
                    more -= cost[j];
                    temp = temp + cost[j]*j;
                }
                else {
                    temp = temp + more*j;
                    more = 0;
                }
                if(more == 0) {
                    break;
                }
            }
            cost[a[i].c] += 1ll*a[i].p;
            ans = min(ans, temp);
        }
        printf("%lld\n", ans);
    }
    return 0;
}

【D】 Number 水题

水题不写了。

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <bitset>
#include <cctype>
#include <cstdio>
#include <vector>
#include <string>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iomanip>
#include <numeric>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double PI = acos(-1.0);
const double eps = 1e-6;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;

int get(int x) {
    int res = 0;
    while(x) {
        res++;
        x /= 10;
    }
    return res;
}

int main() {
    int n, p;
    while(~scanf("%d%d", &n, &p)) {
        int l = get(p);
        if(l > n) {
            printf("T_T\n");
            continue;
        }
        printf("%d", p);
        for(int i = l+1; i <= n; i++) {
            printf("0");
        }
        printf("\n");
    }
    return 0;
}

【E】 Find the median 线段树

参考:http://keyblog.cn/article-189.html

题目花里胡哨的,其实就是给定两个数组 L,R和一个空序列,对于第 \(i\) 对 LR 来说,把 \([L_i,R_i]\) 中的每个数加入序列,然后输出当前序列中的中位数。

主要是写起来麻烦,一些 +1/-1 会很容易错,尤其是我这种使用 \(vector\) 离散化的,我是多插了一个 -1 让下标从 1 开始,这样或者用数组可以规避很多 +1/-1。具体实现看代码。

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <bitset>
#include <cctype>
#include <cstdio>
#include <vector>
#include <string>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iomanip>
#include <numeric>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double PI = acos(-1.0);
const double eps = 1e-6;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;

const int maxn = 8e5+5;

ll x[maxn], y[maxn];
ll val[maxn<<2], lazy[maxn<<2];
vector<ll> v;

int getid(ll x) {
    return lower_bound(v.begin(), v.end(), x) - v.begin();
}

void pushup(int rt) {
    val[rt] = val[rt<<1] + val[rt<<1|1];
}

void pushdown(int l, int r, int rt) {
    int mid = (l+r) >> 1;
    val[rt<<1] += (v[mid]-v[l])*lazy[rt];
    val[rt<<1|1] += (v[r]-v[mid])*lazy[rt];
    lazy[rt<<1] += lazy[rt];
    lazy[rt<<1|1] += lazy[rt];
    lazy[rt] = 0;
}

void build(int l, int r, int rt) {
    if(l == r) {
        val[rt] = lazy[rt] = 0;
        return ;
    }
    int mid = (l+r) >> 1;
    build(l, mid, rt<<1);
    build(mid+1, r, rt<<1|1);
    pushup(rt);
}

void update(int L, int R, int l, int r, int rt) {
    if(L <= l && r-1 <= R) {
        val[rt] += (v[r]-v[l]);
        lazy[rt] ++;
        return ;
    }
    if(lazy[rt]) {
        pushdown(l, r, rt);
    }
    int mid = (l+r) >> 1;
    if(L < mid) {
        update(L, R, l, mid, rt<<1);
    }
    if(R >= mid) {
        update(L, R, mid, r, rt<<1|1);
    }
    pushup(rt);
}

ll query(int l, int r, int rt, ll x) {      // 找第 x 大的数
    if(l == r-1) {
        ll t = val[rt] / (v[r]-v[l]);
        return v[l] + (x-1)/t;
    }
    if(lazy[rt]) {
        pushdown(l, r, rt);
    }
    int mid = (l+r) >> 1;
    if(val[rt<<1] >= x) {
        return query(l, mid, rt<<1, x);
    }
    else {
        return query(mid, r, rt<<1|1, x-val[rt<<1]);
    }
}

int main() {
    int n;
    scanf("%d", &n);
    v.clear();
    ll a1, b1, c1, m1;
    ll a2, b2, c2, m2;
    scanf("%lld%lld%lld%lld%lld%lld", &x[1], &x[2], &a1, &b1, &c1, &m1);
    scanf("%lld%lld%lld%lld%lld%lld", &y[1], &y[2], &a2, &b2, &c2, &m2);
    for(int i = 3; i <= n; i++) {
        x[i] = (a1*x[i-1]+b1*x[i-2]+c1) % m1;
        y[i] = (a2*y[i-1]+b2*y[i-2]+c2) % m2;
    }
    for(int i = 1; i <= n; i++) {
         ll tx = x[i];
        ll ty = y[i];
        x[i] = min(tx, ty) + 1;
        y[i] = max(tx, ty) + 1 + 1; // 多 +1 方便区间操作
        v.push_back(x[i]);
        v.push_back(y[i]);
    }
    v.push_back(-1);
    sort(v.begin(), v.end());
    v.erase(unique(v.begin(), v.end()), v.end());
    int new_n = v.size();
    ll sum = 0;
    build(1, new_n, 1);
    for(int i = 1; i <= n; i++) {
        int l = getid(x[i]);
        int r = getid(y[i]);
        update(l, r-1, 1, new_n, 1);
        sum = sum + (v[r] - v[l]);
        printf("%lld\n", query(1, new_n, 1, (sum+1)/2));
    }
    return 0;
}

【J】 A+B problem 水题

其实不是很想把它叫做水题,毕竟把我给卡了,明明 10 行搞定的东西,脑子一抽写了快 100 行,多花了不知道多久...以下代码为赛后补题

水题不写了。

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <bitset>
#include <cctype>
#include <cstdio>
#include <vector>
#include <string>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iomanip>
#include <numeric>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double PI = acos(-1.0);
const double eps = 1e-6;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;

ll f(ll x) {
    ll res = 0;
    while(x) {
        res = res*10 + x%10;
        x /= 10;
    }
    return res;
}

int main() {
    ll a, b;
    int t;
    scanf("%d", &t);
    while(t--) {
        scanf("%lld%lld", &a, &b);
        printf("%lld\n", f(f(a)+f(b)));
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Decray/p/11366001.html