Avito Code Challenge 2018

 

A. Antipalindrome

直接暴力判断就行了。

#include <bits/stdc++.h>
using namespace std;

const int N = 100;
char s[N];

bool check(int i, int j) {
    while (i <= j) {
        if (s[i] != s[j]) return 0;
        i++; j--;
    }
    return 1;
}

int main() {
    scanf("%s", s);
    int n = strlen(s);
    for (int tail = n - 1; tail >= 0; tail--) {
        if (!check(0, tail)) {
            printf("%d\n", tail + 1);
            return 0;
        }
    }
    puts("0");
    return 0;
}
View Code

 

B. Businessmen Problems

排序或者map都行。

#include <bits/stdc++.h>
#define fi first
#define se second
using namespace std;

const int N = 1e5 + 7;
map<int, int> mp;

int main() {
    int n;
    scanf("%d", &n);
    long long ans = 0;
    for (int i = 1; i <= n; i++) {
        int x, y;
        scanf("%d%d", &x, &y);
        mp[x] = y;
        ans += y;
    }
    int m;
    scanf("%d", &m);
    for (int i = 1; i <= m; i++) {
        int x, y;
        scanf("%d%d", &x, &y);
        if (mp.count(x)) {
            if (mp[x] < y) ans += y - mp[x];
        } else ans += y;
    }
    printf("%lld\n", ans);
    return 0;
}
View Code

 

C. Useful Decomposition

如果整棵树没有一个节点度数大于 $2$,那么显然这棵树就是一条链,输出两个度数为 $1$ 的节点即可。
存在度数大于 $2$ 的,那么这个节点就是那个分解点,看有多少个度数大于等于 $3$ 即可。

#include <bits/stdc++.h>
using namespace std;

const int N = 1e5 + 7;

vector<int> G[N];

int main() {
    int n;
    scanf("%d", &n);
    for (int i = 1, u, v; i < n; i++) {
        scanf("%d%d", &u, &v);
        G[u].push_back(v);
        G[v].push_back(u);
    }
    vector<int> ends;
    int ans = 0;
    for (int i = 1; i <= n; i++) {
        if (G[i].size() >= 3) {
            if (!ans) {
                ans = i;
            } else {
                puts("No");
                return 0;
            }
        } else if (G[i].size() == 1) {
            ends.push_back(i);
        }
    }
    puts("Yes");
    if (!ans) {
        printf("1\n%d %d\n", ends[0], ends[1]);
    } else {
        printf("%d\n", (int)ends.size());
        for (auto v: ends) 
            printf("%d %d\n", ans, v);
    }
    return 0;
}
View Code

 

D. Bookshelves

这是第二次见位运算参与的DP了。
位运算不满足最优子结构的性质,当前是max,不一定后面用到这个值能使答案更大。
那么考虑按位贪心,位越高为 $1$ 肯定越优。
那么从高到低枚举每一位能否为 $1$,再用DP check一下即可。

#include <bits/stdc++.h>
#define ll long long
using namespace std;

const int N = 60;
bool dp[N][N];
ll sum[N];
int n, m;

bool check(ll ans) {
    memset(dp, 0, sizeof(dp));
    dp[0][0] = 1;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            for (int k = 1; k <= i; k++)
                if (((sum[i] - sum[k - 1] & ans) == ans) && dp[k - 1][j - 1]) {
                    dp[i][j] = 1;
                    break;
                }
    return dp[n][m];
}

int main() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++)
        scanf("%lld", &sum[i]), sum[i] += sum[i - 1];
    long long ans = 0;
    for (int i = 58; ~i; i--) {
        if (check(ans | (1LL << i)))
            ans |= (1LL << i);
    }
    printf("%lld\n", ans);
    return 0;
}
View Code

 

E. Addition on Segments

写了一个可撤销的DP。没有意识到会爆long long。WA在75。
一看题解发现加个不容易被卡的模数可行。还真过了。板子里面得备一些奇奇怪怪的模数了。

最开始写的很暴力。扫描线,遇到一个 $l$ 当前multiset就插入这个 $x$,遇到一个 $r$ 就删除。
如果当前位置的值都处理完了,multiset不为空,就做一遍DP,用bitset优化一下,但是显然复杂度能卡到 $O(\dfrac{n ^ 3}{32})$

然后就想到每次都没必要初始化背包,加入一个数就直接加,删除就直接删,也就是正着做减法背包。
复杂度 $O(nq)$。但就是爆long long。加个模数就OK了。

#include <bits/stdc++.h>
#define pii pair<int, int>
#define fi first
#define se second
using namespace std;

const int N = 1e4 + 1;
const int MOD = 402653189;
bitset<N> mask, temp;
vector<pii> p[N];
int dp[N];

void M(int &x) {
    if (x >= MOD) x -= MOD;
    if (x < 0) x += MOD;
}

int main() {
    int n, q;
    scanf("%d%d", &n, &q);
    for (int i = 1; i <= q; i++) {
        int l, r, x;
        scanf("%d%d%d", &l, &r, &x);
        p[l].push_back(pii(x, 1));
        p[r + 1].push_back(pii(x, -1));
    }
    dp[0] = 1;
    for (int i = 1; i <= n; i++) {
        for (auto u: p[i]) {
            if (u.se == -1) {
                for (int i = u.fi; i <= n; i++) {
                    M(dp[i] -= dp[i - u.fi]);
                    if (dp[i]) temp.set(i, 1);
                    else temp.set(i, 0);
                }
            } else {
                for (int i = n; i >= u.fi; i--) {
                    M(dp[i] += dp[i - u.fi]);
                    if (dp[i]) temp.set(i, 1);
                    else temp.set(i, 0);
                }
            }
        }
        mask |= temp;
    }
    vector<int> ans;
    for (int i = 1; i <= n; i++)
        if (mask.test(i)) ans.push_back(i);
    int cnt = 0;
    cnt = ans.size();
    printf("%d\n", cnt);
    for (auto x: ans) {
        printf("%d%c", x, " \n"[cnt == 1]);
        cnt--;
    }
    return 0;
}
View Code

本质就是维护每个节点的数形成的背包长啥样。长啥样具体可以用bitset表示。
那么就用线段树维护bitset。下放标记就行了。
复杂度 $O(\dfrac{nqlogn}{32})$

#include <bits/stdc++.h>
using namespace std;

const int N = 1e4 + 5;

bitset<N> ans;

struct Seg {
    #define lp p << 1
    #define rp p << 1 | 1
    bitset<N> tree[N * 4];
    vector<int> lazy[N * 4];
    void init() {
        for (int i = 1; i < 4 * N; i++)
            tree[i] = 1;
    }
    void update(int p, int l, int r, int x, int y, int v) {
        if (x <= l && y >= r) {
            lazy[p].push_back(v);
            return;
        }
        int mid = l + r >> 1;
        if (x <= mid) update(lp, l, mid, x, y, v);
        if (y > mid) update(rp, mid + 1, r, x, y, v);
    }
    void pushdown(int p) {
        tree[lp] |= tree[p];
        tree[rp] |= tree[p];
    }
    void build(int p, int l, int r) {
        for (auto x: lazy[p]) {
            tree[p] |= (tree[p] << x);
        }
        if (l == r) {
            ans |= tree[p];
            return;
        }
        pushdown(p);
        int mid = l + r >> 1;
        build(lp, l, mid);
        build(rp, mid + 1, r);
    }
} seg;

int main() {
    int n, q;
    scanf("%d%d", &n, &q);
    seg.init();
    for (int i = 1; i <= q; i++) {
        int l, r, x;
        scanf("%d%d%d", &l, &r, &x);
        seg.update(1, 1, n, l, r, x);
    }
    seg.build(1, 1, n);
    int cnt = 0;
    for (int i = 1; i <= n; i++)
        if (ans.test(i)) cnt++;
    printf("%d\n", cnt);
    for (int i = 1; i <= n; i++)
        if (ans.test(i)) printf("%d ", i);
    return 0;
}
View Code

 

F. Round Marriage

二分答案。
环上距离表示为 $min(|a_i - b_j|, L - |a_i - b_j|)$。
拆绝对值就发现只多了 $b_j + L$, $b_j - L$ 这两项。
那么加入到 $b$ 数组里面就可以破环成链了。
把 $a$ $b$ 数组排序
如果 $a_1$ 选了 $b_j$,那么接下来肯定是 $a_2$ 选 $b_{j+1}$ 依次下去肯定是最优的。
所以就看每个 $a_i$ 能在 $b$ 匹配的区间中,能否每个区间取一个数形成一个等差数列。
一种实现方式是对于每一个 $a_i$ 能交的区间 $[l, r]$,左右端点减去 $i$ 变成 $[l - i, r - i]$。就变成了求有没有起点相同。
直接求区间交就可以了。
这个贪心是最优的但不是充要的。
第一个数求到了 $[l, r]$,那么第二个数从 $[l + 1, r + 1]$ 里面找一个合法的区间,这样继续找,只要最后一个数能找的区间存在,那么就是可以的。
双指针扫就行了。

#include <bits/stdc++.h>
using namespace std;

const int N = 2e5 + 7;
int n, a[N], b[N * 4];

bool check(int x) {
    int l = 1, r = 3 * n;
    for (int i = 1; i <= n; i++) {
        while (a[i] - b[l] > x) l++;
        while (b[r] - a[i] > x) r--;
        l++; r++;
    }
    return l <= r;
}

int main() {
    int L;
    scanf("%d%d", &n, &L);
    for (int i = 1; i <= n; i++) 
        scanf("%d", a + i);
    for (int i = 1; i <= n; i++)
        scanf("%d", b + i), b[n + i] = b[i] + L, b[2 * n + i] = b[i] - L;
    sort(a + 1, a + n + 1);
    sort(b + 1, b + 1 + 3 * n);
    int l = 0, r = L, ans = 0;
    while (l <= r) {
        int mid = l + r >> 1;
        if (check(mid)) ans = mid, r = mid - 1;
        else l = mid + 1;
    }
    printf("%d\n", ans);
    return 0;
}
View Code

 

G. Magic multisets

首先对整个区间都乘上 $2$,再对那些没有这个数的乘上 $2$ 的逆元再加 $1$。
第二个步骤是关键。
开 $N$ 个 set,对于每个数 $x$ 的set存一些区间,表示这些区间里都有这个数 $x$。
每次更新操作的区间 $[l, r]$,就相当于把这个set里面与 $[l, r]$ 相交的区间都合并起来。
意会一下感觉总的合并次数最多只有 $n$ 次。

#include <bits/stdc++.h>
#define pii pair<int, int>
#define fi first
#define se second
using namespace std;

const int N = 2e5 + 7;
const int MOD = 998244353;
const int inv2 = (MOD + 1) / 2;

int n, q;

int M(int x) {
    return x >= MOD ? x - MOD : x;
}

struct Seg {
    #define lp p << 1
    #define rp p << 1 | 1
    int tree[N * 4], mul[N * 4], add[N * 4];
    void build(int p, int l, int r) {
        mul[p] = 1;
        if (l == r) return;
        int mid = l + r >> 1;
        build(lp, l, mid);
        build(rp, mid + 1, r);
    }
    inline void pushup(int p) {
        tree[p] = M(tree[lp] + tree[rp]);
    }
    inline void tagm(int p, int val) {
        mul[p] = 1LL * mul[p] * val % MOD;
        add[p] = 1LL * add[p] * val % MOD;
        tree[p] = 1LL * tree[p] * val % MOD;
    }
    inline void taga(int p, int val, int cnt) {
        add[p] = M(add[p] + val);
        tree[p] = M(tree[p] + 1LL * cnt * val % MOD);
    }
    inline void pushdown(int p, int llen, int rlen) {
        if (mul[p] != 1) {
            tagm(lp, mul[p]);
            tagm(rp, mul[p]);
            mul[p] = 1;
        }
        if (add[p]) {
            taga(lp, add[p], llen);
            taga(rp, add[p], rlen);
            add[p] = 0;
        }
    }
    void update_M(int p, int l, int r, int x, int y, int val) {
        if (x <= l && y >= r) {
            tree[p] = 1LL * tree[p] * val % MOD;
            mul[p] = 1LL * mul[p] * val % MOD;
            add[p] = 1LL * add[p] * val % MOD;
            return;
        }
        int mid = l + r >> 1;
        pushdown(p, mid - l + 1, r - mid);
        if (x <= mid) update_M(lp, l, mid, x, y, val);
        if (y > mid) update_M(rp, mid + 1, r, x, y, val);
        pushup(p);
    }
    void update_A(int p, int l, int r, int x, int y, int val) {
        if (x <= l && y >= r) {
            tree[p] = M(tree[p] + 1LL * (r - l + 1) * val % MOD);
            add[p] = M(add[p] + val);
            return;
        }
        int mid = l + r >> 1;
        pushdown(p, mid - l + 1, r - mid);
        if (x <= mid) update_A(lp, l, mid, x, y, val);
        if (y > mid) update_A(rp, mid + 1, r, x, y, val);
        pushup(p);
    }
    int query(int p, int l, int r, int x, int y) {
        if (x <= l && y >= r) return tree[p];
        int mid = l + r >> 1;
        int ans = 0;
        pushdown(p, mid - l + 1, r - mid);
        if (x <= mid) ans = M(ans + query(lp, l, mid, x, y));
        if (y > mid) ans = M(ans + query(rp, mid + 1, r, x, y));
        return ans;
    }
} seg;

set<pii> st[N];

void update(int l, int r, int x) {
    seg.update_M(1, 1, n, l, r, 2);
    set<pii>::iterator it;
    for (it = st[x].lower_bound(pii(l, l)); it != st[x].end(); it++) {
        set<pii>::iterator last = it;
        last--;
        int L = (*last).se + 1, R = (*it).fi - 1;
        L = max(L, l);
        R = min(R, r);
        if (L <= R) {
            seg.update_M(1, 1, n, L, R, inv2);
            seg.update_A(1, 1, n, L, R, 1);
        }
        if ((*it).fi >= r) break;
    }
    it = st[x].upper_bound(pii(l, l)); it--;
    pii p = *it;
    int L = l, R = r;
    if (p.se >= L) L = p.fi;
    it = st[x].upper_bound(pii(r, r)); it--;
    p = *it;
    if (p.se >= R) R = p.se;
    vector<pii> vec;
    for (it = st[x].lower_bound(pii(L, L)); it != st[x].end(); it++) {
        p = *it;
        if (p.fi >= L && p.se <= R) vec.push_back(p);
        else break;
    }
    for (auto p : vec) st[x].erase(p);
    st[x].insert(pii(L, R));
}

int main() {
    //freopen("in.txt", "r", stdin);
    scanf("%d%d", &n, &q);
    seg.build(1, 1, n);
    for (int i = 0; i <= n; i++) 
        st[i].insert(pii(0, 0)), st[i].insert(pii(n + 1, n + 1));
    for (int i = 1, opt, l, r, x; i <= q; i++) {
        scanf("%d%d%d", &opt, &l, &r);
        if (opt == 1) {
            scanf("%d", &x);
            update(l, r, x);
        } else {
            printf("%d\n", seg.query(1, 1, n, l, r));
        }
    }
    return 0;
}
View Code

 

posted @ 2019-10-14 22:00  Mrzdtz220  阅读(121)  评论(0编辑  收藏  举报