18/9/9牛客网提高组Day1

      牛客网提高组Day1

T1 中位数

  这好像是主席树??听说过,不会啊。。。

  最后只打了个暴力,可能是n2logn?

  只过了前30%  qwq

#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
const int M = 100005;
int n, len;
int tmp, ans;
int a[M];
struct nond {
    int id, num;
}e[M];

bool cmp1(nond x, nond y) {
    return x.num < y.num;
}
bool cmp2(nond x, nond y) {
    return x.id < y.id;
}

int main() {
    scanf("%d%d", &n, &len);
    for (int i = 1; i <= n; i++) {
        scanf("%d", &e[i].num);
        e[i].id = i;
    }
    for (int i = 1; i + len - 1 <= n; i++)
        for (int j = i + len - 1; j <= n; j++) {
            sort(e + i, e + j + 1, cmp1);
            if ((j - i + 1) & 1) tmp = e[(j + i) / 2].num;
            else tmp = e[(j + i - 1) / 2].num;
            ans = max(ans, tmp);
            sort(e + i, e + j, cmp2);
        }
    printf("%d\n", ans);
    return 0;
}
考场代码

正解:

  二分求最终可能的答案x,把>x的数字记为1,<=x的数字记为-1。检验是否存在和>=0的区间。可以维护前缀和的前缀最小值

 复杂度:O(nlogA[i])

#include <bits/stdc++.h>
using namespace std;
const int maxn = 100005;
int a[maxn], n, L;

bool test(int z) {
    static int b[maxn];
    for(int i = 1; i <= n; i ++)
        if (a[i] >= z) b[i] = 1;
        else b[i] = -1;
    for(int i = 1, mi = (1 << 30); i <= n; i ++) {
        if (i >= L) mi = min(mi, b[i - L]);
        b[i] += b[i - 1];
        if (i >= L && b[i] - mi > 0) return 1;
    }
    return 0;
}

int main() {
//    freopen("median.in","r",stdin);
//    freopen("median.out","w",stdout);
    scanf("%d%d", &n, &L);
    for(int i = 1; i <= n; i ++) scanf("%d", &a[i]);
    int l = 0, r = int(1e9), tmp;
    for(int mid; l <= r;) {
        mid = l + r >> 1;
        if (test(mid)) tmp = mid, l = mid + 1;
        else r = mid - 1;
    }
    printf("%d\n", tmp);
    return 0;
}
std

 

T2 数数字

  暴力枚举区间[L, R]中的每一个数,用一个check函数判断每个数的每个位上的数的乘积是否在区间[L1, R1]中,记录答案

  本来还想拿L1, R1<=1000的20分来,然后。。。yy了一会,没想出来咋写,so。。只交了40分暴力

#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
typedef long long LL;
LL l, r, l1, r1;
LL ans;

LL check2(int x) {
    LL res = 1;
    while(x) {
        int tmp = x % 10;
        res *= tmp;
        x /= 10;
    }
    if(res >= l1 && res <= r1) return 1;
    else return 0;
}

int main() {
    scanf("%lld%lld%lld%lld", &l, &r, &l1, &r1);
    for(int i = l; i <= r; i++)
        if(check2(i)) ans++;
    printf("%lld\n", ans);
    return 0;
}
考场代码

正解:

  记忆化搜索或数位DP

  这几天刚好在做数位DP的题,然而并不会 qwq

 DP做法:

  由于数位乘积只是由0到9,可以把L1,R1分类讨论,假如区间包含0,则原来的数字分为包含至少一个零,和完全不包含0两类。前一类可以使用简单的数位DP计算。接下来介绍后一类。

  由于只包含1到9,所以只用记录乘积的质因数分解中,出现了多少2,3,5,7。题目中的L1,R1不超过10^18,可以计算出2最多为59个,3最多为37, 5最多为26,7最多为21。设F[i][c_2][c_3][c_5][c_7]表示考虑到第i位,乘积状态为c_2,c_3,c_5,c_7的数位DP情况。接着就是经典数位DP做法。空间可能比较紧,需要用滚动数组。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int co[10][10];
ll l, r, l1, r1;
ll pw[10][100];

inline int merge(int i, int cr, int b) {
    return (cr > b ? 2 : (cr == b ? i : 0));
}

ll calc_no_zero(ll x) {
    if (x <= 0) return 0;
    static ll f[2][3][60][38][27][22];
    memset(f,0,sizeof f);
    int cr = 0;
    f[0][1][0][0][0][0] = 1;
    ll ans = 0, val;
    for(; x; x /= 10, cr ^= 1) {
        int bit = x % 10;
        memset(f[cr ^ 1], 0, sizeof f[cr]);
        for(int i = 0; i < 3; i ++)
            for(int n_2 = 0; n_2 < 60; n_2 ++)
                for(int n_3 = 0; n_3 < 38; n_3 ++)
                    for(int n_5 = 0; n_5 < 27; n_5 ++)
                        for(int n_7 = 0; n_7 < 22; n_7 ++)
                            if (val = f[cr][i][n_2][n_3][n_5][n_7]) {
                                ll q1 = r1 / pw[2][n_2] / pw[3][n_3] / pw[5][n_5] / pw[7][n_7];
                                ll p1 = (l1 - 1) / pw[2][n_2] / pw[3][n_3] / pw[5][n_5] / pw[7][n_7];
                                if (q1 >= 1 && p1 <= 0) ans += val;
                                if (q1 <= 0) continue;
                                for(int p = 1; p < 10; p ++) {
                                    int ni = merge(i, p, bit);
                                    int c_2 = n_2 + co[p][2];
                                    int c_3 = n_3 + co[p][3];
                                    int c_5 = n_5 + co[p][5];
                                    int c_7 = n_7 + co[p][7];
                                    if (c_2 >= 60 || c_3 >= 38 || c_5 >= 27 || c_7 >= 22) continue;
                                    f[cr ^ 1][ni][c_2][c_3][c_5][c_7] += val;
                                }
                            }
    }
    for(int i = 0; i < 2; i ++)
        for(int n_2 = 0; n_2 < 60; n_2 ++)
            for(int n_3 = 0; n_3 < 38; n_3 ++)
                for(int n_5 = 0; n_5 < 27; n_5 ++)
                    for(int n_7 = 0; n_7 < 22; n_7 ++)
                        if (val = f[cr][i][n_2][n_3][n_5][n_7]) {
                            ll q1 = r1 / pw[2][n_2] / pw[3][n_3] / pw[5][n_5] / pw[7][n_7];
                            ll p1 = (l1 - 1) / pw[2][n_2] / pw[3][n_3] / pw[5][n_5] / pw[7][n_7];
                            if (q1 >= 1 && p1 <= 0) ans += val;
                        }
    return ans;
}

ll calc_with_zero(ll x) {
    if (l1) return 0;
    static ll g[2][3][2][2];
    memset(g, 0, sizeof g);
    int cr = 0;
    ll ans = 0;
    g[0][1][0][0] = 1;
    for(; x; x /= 10, cr ^= 1) {
        int bit = x % 10;
        memset(g[cr ^ 1], 0, sizeof g[cr]);
        for(int i = 0; i < 3; i ++)
            for(int j = 0; j < 2; j ++)
                for(int hd = 0; hd < 2; hd ++)
                    if (g[cr][i][j][hd]) {
                        if (hd && j) ans += g[cr][i][j][hd];
                        for(int p = 0; p < 10; p ++) {
                            int ni = merge(i, p, bit);
                            int nj = (j | (p == 0));
                            int hdt = (p > 0);
                            g[cr ^ 1][ni][nj][hdt] += g[cr][i][j][hd];
                        }
                    }
    }
    return ans + g[cr][0][1][1] + g[cr][1][1][1];
}

ll calc(long long x) {
    return calc_no_zero(x) + calc_with_zero(x);
}

int main() {
//    freopen("count.in","r",stdin);
//    freopen("count.out","w",stdout);
    for(int i = 1; i < 10; i ++) {
        for(int j = 2; j < 10; j ++)
            for(int k = i; k % j == 0; k /= j)
                co[i][j] ++;
        pw[i][0] = 1;
        for(int j = 1; j <= 60; j ++)
            pw[i][j] = pw[i][j - 1] * i;
    }
    cin >> l >> r >> l1 >> r1;
    ll ans = 0;
    if (!l) ans += (l1 == 0), ++ l;
    if (l <= r) ans += calc(r) - calc(l - 1);
    cout << ans << endl;
    return 0;
}
std

 

T3 保护

  表示只能想到最暴力的方法:

   建树后,根据军队保护的范围枚举,为边加边权,然后每个重要人物到根节点的路径也挨个枚举   但是好像写挂了,一分没有 qwq

  想过树剖+线段树的做法:

   树剖,然后线段树维护区间最小值,军队的保护可以做线段树的区间加法

   也不知道对不对的做法,关键是不会啊  弱的一批

  好像还有些用倍增做的童鞋,然而出了奇奇怪怪的错误,导致。。。

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <cmath>
using namespace std;
const int M = 200005;
int n, m, t, tot;
int cap[M*2];
int to[M*2], net[M*2], head[M];
int deep[M], top[M], size[M], dad[M];
struct nond {
    int id, sum;
    int v, k;
} e[M];

void add(int u, int v) {
    to[++tot] = v; net[tot] = head[u]; head[u] = tot;
    to[++tot] = u; net[tot] = head[v]; head[v] = tot;
}

void dfs(int now) {
    size[now] = 1;
    deep[now] = deep[dad[now]] + 1;
    for (int i = head[now]; i; i = net[i])
        if (to[i] != dad[now]) {
            dad[to[i]] = now;
            dfs(to[i]);
            size[now] += size[to[i]];
        }
}

void dfsl(int now) {
    int t = 0;
    if (!top[now]) top[now] = now;
    for (int i = head[now]; i; i = net[i])
        if (to[i] != dad[now] && size[t] > size[to[i]])
            t = to[i];
    if (t) {
        top[t] = top[now];
        dfsl(t);
    }
    for (int i = head[now]; i; i = net[i])
        if (to[i] != dad[now] && to[i] != t)
            dfsl(to[i]);
}

int lca(int x, int y) {
    while (top[x] != top[y]) {
        if (deep[top[x]] < deep[top[y]])
            swap(x, y);
        x = dad[top[x]];
    }
    return deep[x] > deep[y] ? y : x;
}

void change(int u, int v) {
    for (int i = head[v]; i; i = net[i]) {
        if (to[i] == u) {
            for (int j = head[u]; j; j = net[j])
                if (to[j] == v) { cap[j]++; break; }
            cap[i]++; return ;
        }
        if (to[i] = dad[v]) {
            cap[i]++;
            for (int j = head[u]; j; j = net[j])
                if (to[j] == v) cap[j]++;
        } v = dad[v];
    }
}

void fun(int now) {
    int u = e[now].v, tmp = e[now].k;
    for (int i = head[u]; i; i = net[i]) {
        if (to[i] == 1) if (cap[i] >= tmp) { e[now].sum++; return ; }
        if (to[i] == dad[u])
            if (cap[i] >= tmp) e[now].sum++, u = dad[u];
    }
}

bool cmp1(nond x, nond y) {
    if (deep[x.v] == deep[y.v]) return x.k > y.k;
    return deep[x.v] > deep[y.v];
}
bool cmp2(nond x, nond y) {
    return x.id < y.id;
}

int main() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i < n; i++) {
        int u, v;
        scanf("%d%d", &u, &v);
        add(u, v);
    }
    dfs(1); dfsl(1);
    for (int i = 1; i <= m; i++) {
        int u, v;
        scanf("%d%d", &u, &v);
        if (u == v) continue;
        int S = lca(u, v);
//        printf("%d\n", S);
        if (S == u) change(S, v);
        else if (S == v) change(S, u);
        else change(S, u), change(S, v);
    }
    scanf("%d", &t);
    for (int i = 1; i <= t; i++) 
        scanf("%d%d", &e[i].v, &e[i].k);
    sort(e + 1, e + t + 1, cmp1);
    for (int i = 1; i <= t; i++)
        if (e[i].v == e[i - 1].v) e[i].sum = e[i - 1].sum;
        else fun(i);
    sort(e + 1, e + t + 1, cmp2);
    for (int i = 1; i <= t; i++)
        printf("%d\n", e[i].sum);
    return 0;
}
考场代码,求路过dalao指正 qwq

正解:

  启发式合并维护子树的线段树。询问时可以在维护出来的u的那棵线段树上走,相当于找第k小数。复杂度O((n+q)logn)。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 200005;
struct node {
    int l,r,cnt;
} T[maxn * 100];

vector<int> lk[maxn];
int fa[maxn][20], dep[maxn], ord[maxn], cnt, n, m, ans[maxn];

void dfs(int now, int pre) {
    dep[now] = dep[pre] + 1;
    fa[now][0] = pre;
    for (int i = 1; (1 << i) <= dep[now]; i++) fa[now][i] = fa[fa[now][i - 1]][i - 1];
    for (int i = 0; i < lk[now].size(); i ++)
        if (lk[now][i] != pre)
            dfs(lk[now][i], now);
}

void insert(int l, int r, int p, int &jd) {
    if (!jd) jd = ++ cnt;
    T[jd].cnt ++;
    if (l == r) return;
    int mid = l + r >> 1;
    if (p <= mid) insert(l, mid, p, T[jd].l);
    else insert(mid + 1, r, p, T[jd].r);
}

int merge(int l, int r, int a, int b) {
    if ((!a) || (!b)) return a + b;
    int jd = ++ cnt;
    T[jd].cnt = T[a].cnt + T[b].cnt;
    if (l == r) return jd;
    int mid = l + r >> 1;
    T[jd].l = merge(l, mid, T[a].l, T[b].l);
    T[jd].r = merge(mid + 1, r, T[a].r, T[b].r);
    return jd;
}

int find_k(int l, int r, int k, int jd) {
    if (T[jd].cnt < k) return (1 << 30);
    if (l == r) return l;
    int mid = l + r >> 1;
    if (T[T[jd].l].cnt >= k) return find_k(l, mid, k, T[jd].l);
    return find_k(mid + 1, r, k - T[T[jd].l].cnt, T[jd].r);
}

void dfs_w(int now, int pre) {
    for(int i = 0; i < lk[now].size(); i ++)
        if (lk[now][i] != pre) {
            dfs_w(lk[now][i], now);
            ord[now] = merge(1, n, ord[now], ord[lk[now][i]]);
        }
}

int get_lca(int u, int v) {
    if (dep[u] > dep[v]) swap(u,v);
    for(int i = 18; i + 1; i --)
        if (dep[fa[v][i]] >= dep[u]) v = fa[v][i];
    if (u == v) return u;
    for(int i = 18; i + 1; i --)
        if (fa[v][i] != fa[u][i]) v = fa[v][i], u = fa[u][i];
    return fa[u][0];
}

int main() {
//    freopen("guard.in","r",stdin);
//    freopen("guard.out","w",stdout);
    scanf("%d%d", &n, &m);
    for(int i = 1, u, v; i < n; i ++) {
        scanf("%d%d", &u, &v);
        lk[u].push_back(v), lk[v].push_back(u);
    }
    dfs(1, 0);
    for(int i = 1, u, v; i <= m; i ++) {
        scanf("%d%d", &u, &v);
        int p = get_lca(u, v);
        insert(1, n, dep[p], ord[u]), insert(1, n, dep[p], ord[v]);
    }
    dfs_w(1, 0);
    int q;
    scanf("%d", &q);
    for(int i = 1; i <= q; i ++) {
        int u, k;
        scanf("%d%d", &u, &k);
        printf("%d\n", max(0, dep[u] - find_k(1, n, k, ord[u])));
    }
    return 0;
}
std

 

posted @ 2018-09-09 17:29  落云小师妹  阅读(195)  评论(0编辑  收藏  举报