2019 Multi-University Training Contest 6

2019 Multi-University Training Contest 6

B.Nonsense Time

首先有这样一个结论:随机生成序列的期望\(LIS\)长度为\(O(\sqrt{n})\)
然后就可以愉快的暴力了。
考虑逆序时间,即每次删去一个数,并回答询问。
因为限制\(LIS\)的长度为\(\sqrt{n}\),那么期望删除\(\sqrt{n}\)次才会修改\(LIS\)长度,这个时候暴力更新\(LIS\)即可。
复杂度\(O(nlogn\sqrt{n})\),求\(LIS\)并且得到\(LIS\)序列可用树状数组来搞,复杂度是\(O(nlogn)\)的。

Code
#include <bits/stdc++.h>
using namespace std;
const int N = 5e5 + 5;
int T, n;
int a[N], b[N], c[N], d[N], nxt[N], pre[N];
bool used[N];
int f[N], g[N];
int lowbit(int x) {
    return x & (-x);
}
int query(int x) {
    int ans = 0, p = 0;
    for(; x; x -= lowbit(x)) {
        if(c[x] > ans) {
            ans = c[x];
            p = d[x];
        }
    }
    return p;
}
void update(int p, int x, int v) {
    for(; x < N; x += lowbit(x)) {
        if(c[x] < v) {
            c[x] = v; d[x] = p;
        }
    }
}
int build() {
    int tot = 0, p = 0;
    for(int i = 1; i <= n; i++) used[i] = 0;
    for(int i = nxt[0]; i <= n; i = nxt[i]) {
        int k = query(a[i] - 1);
        f[i] = f[k] + 1;
        if(f[i] > tot) tot = f[i], p = i;
        update(i, a[i], f[i]);
        g[i] = k;
    }
    used[p] = 1;
    while(g[p]) used[p = g[p]] = 1;
    for(int i = 1; i <= n; i++)
        for(int j = i; j < N; j += lowbit(j)) c[j] = d[j] = 0;
    return tot;
}
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];
        for(int i = 1; i <= n; i++) cin >> b[i];
        for(int i = 0; i <= n; i++) nxt[i] = i + 1;
        for(int i = n + 1; i; i--) pre[i] = i - 1;
        int ans = build();
        vector <int> res;
        for(int i = n; i >= 1; i--) {
            res.push_back(ans);
            pre[nxt[b[i]]] = pre[b[i]];
            nxt[pre[b[i]]] = nxt[b[i]];
            if(used[b[i]]) ans = build();
        }
        reverse(res.begin(), res.end());
        for(int i = 0; i < res.size(); i++) cout << res[i] << " \n"[i == res.size() - 1];
    }
    return 0;
}

E.Snowy Smile

先对横纵坐标离散化,之后\(O(n^2)\)枚举上下边界,下边界扫过去时维护\(y\)的和,之后解决的就是一个最大连续子段和的问题。
因为是动态维护,考虑线段树。详见代码:

Code
#include <bits/stdc++.h>
#define MP make_pair
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 2005;
int T;
int x[N], y[N], w[N];
int a[N], b[N];
struct SEG{
    ll sum[N << 2], maxv[N << 2], lmax[N << 2], rmax[N << 2];
    void push_up(int o) {
        sum[o] = sum[o << 1] + sum[o << 1|1];
        maxv[o] = max(maxv[o << 1], max(maxv[o << 1|1], rmax[o << 1] + lmax[o << 1|1]));
        lmax[o] = max(lmax[o << 1], sum[o << 1] + lmax[o << 1|1]);
        rmax[o] = max(rmax[o << 1|1], sum[o << 1|1] + rmax[o << 1]);
    }
    void build(int o, int l, int r) {
        sum[o] = maxv[o] = lmax[o] = rmax[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 v) {
        if(l == r) {
            sum[o] += v;
            maxv[o] = lmax[o] = rmax[o] = sum[o];
            return ;
        }
        int mid = (l + r) >> 1;
        if(p <= mid) update(o << 1, l, mid, p, v);
        else update(o << 1|1, mid + 1, r, p, v);
        push_up(o);
    }
}seg;
int main() {
    ios::sync_with_stdio(false); cin.tie(0);
    cin >> T;
    while(T--) {
        a[0] = b[0] = 0;
        int n; cin >> n;
        for(int i = 1; i <= n; i++) {
            cin >> x[i] >> y[i] >> w[i];
            a[++a[0]] = x[i];
            b[++b[0]] = y[i];
        }
        sort(a + 1, a + a[0] + 1);
        sort(b + 1, b + b[0] + 1);
        a[0] = unique(a + 1, a + a[0] + 1) - a - 1;
        b[0] = unique(b + 1, b + b[0] + 1) - b - 1;
        vector <pii> v[N];
        for(int i = 1; i <= n; i++) {
            x[i] = lower_bound(a + 1, a + a[0] + 1, x[i]) - a;
            y[i] = lower_bound(b + 1, b + b[0] + 1, y[i]) - b;
            v[x[i]].push_back(MP(y[i], w[i]));
        }
        ll ans = 0;
        for(int i = 1; i <= a[0]; i++) {
            seg.build(1, 1, b[0]);
            for(int j = i; j <= a[0]; j++) {
                for(auto P : v[j]) {
                    seg.update(1, 1, b[0], P.first, P.second);
                }
                ans = max(ans, seg.maxv[1]);
            }
        }
        cout << ans << '\n';
    }
    return 0;
}

F.Faraway

每个点会将平面分为四个部分,在每个部分距离的绝对值是可以直接去掉的。
最终被\(n\)个点划分的平面有\(n^2\)个,那么就枚举每一个平面,考虑统计答案。因为\(lcm(2,3,4,5)=60\),所以我们\(60*60\)枚举横纵坐标,判合法之后统计答案即可。

Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 15;
int T;
int n, m;
int x[N], y[N], k[N], t[N];
int a[N], b[N];
int na, nb;
bool check(int X, int Y) {
    for(int i = 1; i <= n; i++) {
        if((abs(X - x[i]) + abs(Y - y[i])) % k[i] != t[i]) return 0;
    }
    return 1;
}
ll calc(int l, int r) {
    int len = r - l - 1;
    if(len < 0) return 0;
    return len / 60 + 1;
}
int main() {
    ios::sync_with_stdio(false); cin.tie(0);
    cin >> T;
    while(T--) {
        cin >> n >> m;
        a[na = 1] = b[nb = 1] = m + 1;
        for(int i = 1; i <= n; i++) {
            cin >> x[i] >> y[i] >> k[i] >> t[i];
            a[++na] = x[i]; b[++nb] = y[i];
        }
        sort(a + 1, a + na + 1);
        sort(b + 1, b + nb + 1);
        ll ans = 0;
        for(int i = 0; i < na; i++) if(a[i] < a[i + 1])
            for(int j = 0; j < nb; j++) if(b[j] < b[j + 1])
                for(int X = 0; X < 60; X++) {
                    for(int Y = 0; Y < 60; Y++) {
                        if(check(a[i] + X, b[j] + Y))
                            ans += calc(a[i] + X, a[i + 1]) * calc(b[j] + Y, b[j + 1]);
                    }
                }
        cout << ans << '\n';
    }
    return 0;
}

K.11 Dimensions

先求出\(dp[i,j]\),表示处理了后面\(i\)位,且模\(m\)余数为\(j\)的总方案数。
因为题目要求字典序第\(k\)小,所以就考虑逐位确定。注意到有很多个"?"的话,前面很多都是取\(0\)的,因为每个位置有\(10\)种选择,\(20\)个位置就有\(10^{20}\)种选择了,所以逐位确定时只用考虑后面\(20\)个位置。
因为\(dp\)已经求出了方案,后面直接gao就行。
代码如下:

Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N = 50005, MOD = 1e9 + 7;
const ll inf = 1ll << 61;
int T;
int n, m, q;
ll pw[N], pwm[N];
ll dp[N][22];
char s[N];
int main() {
    ios::sync_with_stdio(false); cin.tie(0);
    cin >> T;
    pw[0] = pwm[0] = 1;
    for(int i = 1; i < N; i++) pw[i] = pw[i - 1] * 10 % MOD;
    vector <int> p;
    while(T--) {
        p.clear();
        cin >> n >> m >> q >> s + 1;
        for(int i = 1; i <= n + 1; i++) pwm[i] = pwm[i - 1] * 10 % m;
        for(int i = 0; i <= n; i++) {
            for(int j = 0; j < m; j++) dp[i][j] = 0;
        }
        dp[0][0] = 1;
        ll ans = 0, sum = 0;
        for(int i = 1; i <= n; i++) {
            char ch = s[n - i + 1];
            if(ch == '?') {
                p.push_back(i - 1);
                for(int j = 0; j < 10; j++) {
                    for(int k = 0; k < m; k++) {
                        int tmp = (j * pwm[i - 1] % m + k) % m;
                        dp[i][tmp] += dp[i - 1][k];
                        if(dp[i][tmp] > inf) dp[i][tmp] = inf;
                    }
                }
            } else {
                for(int k = 0; k < m; k++) {
                    dp[i][k] += dp[i - 1][k];
                    if(dp[i][k] > inf) dp[i][k] = inf;
                }
                ans = (ans + (ch - '0') * pw[i - 1]) % MOD;
                sum = (sum + (ch - '0') * pwm[i - 1]) % m;
            }
        }
        int c = min(30, (int)p.size());
        ll Ans = ans;
        while(q--) {
            ll k; cin >> k;
            ans = Ans;
            int f = (m - sum) % m;
            if(dp[n][f] < k) {
                cout << -1 << '\n';
                continue;
            }
            f = sum;
            for(int i = c - 1; i >= 0; i--) {
                for(int j = 0; j < 10; j++) {
                    int now = (m - (f + j * pwm[p[i]]) % m) % m;
                    if(dp[p[i]][now] >= k) {
                        f = (f + j * pwm[p[i]]) % m;
                        ans = (ans + 1ll * j * pw[p[i]]) % MOD;
                        break;
                    } else k -= dp[p[i]][now];
                }
            }
            cout << ans << '\n';
        }
    }
    return 0;
}

H.TDL

\((f(n,m)-n)\) ^ \(n=k ->f(n,m)=n\) ^ \(k\)
打表发现\(f(n,m)\)在给定数据范围内好像不超过600,那么暴力枚举\(n\)\(k-600\)~\(k+600\)就行了。

Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int T;
ll k;
int m;
int main() {
    ios::sync_with_stdio(false); cin.tie(0);
    cin >> T;
    while(T--) {
        cin >> k >> m;
        ll ans = -1;
        int f = 1;
        for(ll i = max(1ll, k - 650); f && i <= k + 650; i++) {
            int cnt = 0;
            for(ll j = i + 1;; j++) {
                if(__gcd(i, (ll)j) == 1) cnt++;
                if(cnt == m) {
                    if((i + (i ^ k)) == j) f = 0, ans = i;
                    break;
                }
            }
        }
        cout << ans << '\n';
    }
    return 0;
}

L.Stay Real

签到题。贪心取即可。

Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 1e5 + 5, INF = 0x3f3f3f3f;
int t, n, a[MAXN];
int main() {
    ios::sync_with_stdio(false); cin.tie(0);
    cin >> t;
    while (t--) {
        ll ans[2] = { 0 };
        cin >> n;
        for (int i = 1; i <= n; i++) {
            cin >> a[i];
        }
        sort(a + 1, a + 1 + n, greater<int>());
        int now = 0;
        for (int i = 1; i <= n; i++) {
            ans[now] += a[i];
            now ^= 1;
        }
        cout << ans[0] << ' ' << ans[1] << '\n';
    }
    return 0;
}
posted @ 2019-08-10 19:47  heyuhhh  阅读(220)  评论(0编辑  收藏  举报