牛客练习赛25

A 因数个数和

ps:模板题

LL ac(int n)

{

    LL ans=0;

    for(int i=1,temp;i<=n;i=temp+1)

    {

        temp=n/(n/i);

        ans+=(n/i)*(temp-i+1);

    }

    return ans;

}
View Code

最长区间

题解:线段树维护区间左端点,右端点,中间部分的最长递增子区间的长度。

inline void upd(int &x, int y) { x < y && (x = y); }

const int N = 100005;

int n, m, tot;
int L[4 * N], R[4 * N], sum[4 * N], b[N];

void Pushup(int l, int r, int root) {
    int mid = (l + r) >> 1;

    L[root] = L[lson];
    if (L[lson] == mid - l + 1 && b[mid] < b[mid + 1]) L[root] += L[rson];

    R[root] = R[rson];
    if (R[rson] == r - mid && b[mid] < b[mid + 1]) R[root] += R[lson];

    sum[root] = max(sum[lson], sum[rson]);
    if (b[mid] < b[mid + 1]) upd(sum[root], L[rson] + R[lson]);

    //cout << root << " " << L[root] << " " << R[root] << " " << sum[root] << endl;
}

void Build(int l, int r, int root) {
    if (l == r) {
        int x; sc(x);
        b[++tot] = x;
        L[root] = R[root] = sum[root] = 1;
        return;
    }
    int mid = (l + r) >> 1;
    Build(l, mid, lson);
    Build(mid + 1, r, rson);
    Pushup(l, r, root);
}

void Update(int l, int r, int root, int pos, int x) {
    if (l == r) {
        b[pos] = x;
        return;
    }
    int mid = (l + r) >> 1;
    if (pos <= mid) Update(l, mid, lson, pos, x);
    else Update(mid + 1, r, rson, pos, x);
    Pushup(l, r, root);
}



int main()
{
    sc(n), sc(m);
    Build(1, n, 1);
    int res = max(sum[1], max(L[1], R[1]));
    pr(res);

    res = 0;
    while(m--) {
        int pos, x;
        sc(pos), sc(x);
        Update(1, n, 1, pos, x);
        res = max(sum[1], max(L[1], R[1]));
        pr(res);
    }

    return 0;
}
View Code

再编号

ps:找规律,每一项出现的次数是能递推的。

const int N = 100005;
const LL mod = 1000000007;

int n, m;
LL a[N], s[N];

void Inite() {
    a[1] = 0;
    rep(i, 2, N) {
        if (i & 1) a[i] = ((n - 1) * a[i - 1] % mod - n + 1 + mod) % mod;
        else a[i] = ((n - 1) * a[i - 1] % mod + n - 1) % mod;
    }
}

int main()
{
    cin >> n >> m;

    Inite();

    LL sum = 0;
    Rep(i, 1, n) cin >> s[i], sum += s[i];

    sum %= mod;
    while(m--) {
        int x, t;
        sc(x), sc(t);
        if (!t) {
            printf("%lld\n", s[x]);
        }
        else {
            LL ans;
            if (t & 1) {
                ans = ((a[t] + 1) * sum % mod - s[x] + mod) % mod;
            }
            else {
                ans = ((a[t] - 1) * sum % mod + s[x]) % mod;
            }
            printf("%lld\n", ans);
        }
    }
    return 0;
}
View Code

 D a-贝利福斯数

ps:ax + 1 = (ay + 1)(az + 1),x = ayz + y + z,{ x,y,z∈(1,2,3,······,d)},看见这种式子想到了线性筛法,然而实现不了,orz。

暴力枚举,加一个小优化:i 一定是没被标记过数。

void Inite(int m) {
    for (LL i = 1; i <= m; ++i) if (!prime[i]) {
        res.pb(i);
        for (LL j = i; a * i * j + i + j <= m; j++) prime[a * i * j + i + j] = 1;
    }
}

 

const int N = 20000007;

int a, n;
bool vis[N], prime[N];

vector<int> res;

void Inite(int m) {
    Rep(i, 1, m) if (!prime[i]) {
        res.pb(i);
        for (LL j = 1ll * a * i * i + 2 * i; j <= m; j += a * i + 1) prime[j] = 1;
    }
}

void Solve() {
    int d = (n - 1) / a;
    Inite(d);

    rep(i, 0, Size(res)) rep(j, i, Size(res)) {
        LL tp = 1ll * a * res[i] * res[j] + res[i] + res[j];
        if (tp > d) break;
        vis[tp] = 1;
    }
    int cnt = 0;
    Rep(i, 1, d) if (vis[i]) cnt++;
    cout << cnt << endl;
}

int main()
{
    cin >> a >> n;
    Solve();
    return 0;
}
View Code

 E. 定向

题解:将无向图看作是有向图,又因为是无向图,所以添加的双向边在tarjan过程中只能用一条边且只经过一次(代码中的vis[ ]),然后跑一遍普通的tarjan算法就行了。如果只有一个强连通分量,且这个图是联通的,则存在方案。否则就impossible。

inline void upd(int &x, int y) { x < y && (x = y); }

const int N = 1000005;

int n, m, cnt, tot, block;
int DFN[N], LOW[N], pa[N], head[N], ans[N];

bool use[N], vis[N];

stack<int> S;

struct node { int to, next, id, dd; } e[2 * N];

void Inite() {
    block = tot = cnt = 0;
    mem(head, -1), mem(use, 0);
}

void addedge(int u, int v, int id, int dd) {
    e[tot].to = v, e[tot].id = id, e[tot].dd = dd, e[tot].next = head[u], head[u] = tot++;
}

void Tarjan(int u, int p) {
    pa[u] = p;
    DFN[u] = LOW[u] = ++cnt;
    use[u] = 1;
    S.push(u);
    for (int i = head[u]; ~i; i = e[i].next) if (e[i].to != p && !vis[e[i].dd]) {
        vis[e[i].dd] = 1;
        int v = e[i].to;
        ans[e[i].dd] = e[i].id;
        if (!DFN[v]) {
            Tarjan(v, u);
            LOW[u] = min(LOW[u], LOW[v]);
        }
        else if (use[v]) {
            LOW[u] = min(LOW[u], DFN[v]);
        }
    }
    if (DFN[u] == LOW[u]) {
        block++;
        while(S.top() != u) {
            use[S.top()] = 0;
            S.pop();
        }
        use[u] = 0;
        S.pop();
    }
}

void Solve() {
    Tarjan(1, 0);

    bool flag = 0;
    Rep(i, 1, n) if (!DFN[i]) {
        flag = 1;
        break;
    }

    if (flag || block != 1) {
        puts("impossible");
    }
    else {
        Rep(i, 1, m) printf("%d", ans[i]);
    }
}

int main()
{
    Inite();

    sc(n), sc(m);
    Rep(i, 1, m) {
        int u, v;
        sc(u), sc(v);
        addedge(u, v, 1, i);
        addedge(v, u, 0, i);
    }

    Solve();
    return 0;
}
View Code

 F. 青蛙

ps:关键点,青蛙过河的队形是一段连续的区间,考虑最左边的青蛙先跳,然后递推就行了。做题总抓不住关键点 ~

 

posted @ 2018-08-24 22:23  天之道,利而不害  阅读(218)  评论(0编辑  收藏  举报