LOJ刷题记录:2012-2017(SCOI2016)

来源:互联网 发布:淘宝头像设计制作 编辑:程序博客网 时间:2024/06/07 12:13

LOJ刷题记录:2012-2017(SCOI2016)


loj#2012. 「SCOI2016」背单词

神贪心…

所有串翻转,考虑建一棵这样的树,ij当且仅当sisj最长的前缀。这棵树可以用trie建出来。第一种方案显然不会采纳,因此每一个元素放完后,才能放他的子树,而且要按照从子树大小由小到大的放…

#include <bits/stdc++.h>using namespace std;const int MAXN = 600005;int chl[MAXN][26], top = 0, root = 0;int fin[MAXN];void push(int &nd, const char *str){    if (!nd) nd = ++top;    if (*str == '\0') fin[nd] = 1;    else push(chl[nd][*str-'a'], str+1);}int n;char str[MAXN];vector<int> v[MAXN];int siz[MAXN];int dfn[MAXN], dfn_top = 0;long long ans = 0;void dfs_build(int nd, int last){    if (last && fin[nd]) v[last].push_back(nd), last = nd;    if (!last) last = nd;    for (int i = 0; i < 26; i++)        if (chl[nd][i])            dfs_build(chl[nd][i], last);}void dfs_siz(int nd){    siz[nd] = 1;    for (auto i : v[nd]) {        dfs_siz(i);        siz[nd] += siz[i];    }}bool cmp(int i, int j){ return siz[i] < siz[j]; }void calc(int nd, int f){    dfn[nd] = ++dfn_top;    if (nd != root) ans += dfn[nd]-dfn[f];    sort(v[nd].begin(), v[nd].end(), cmp);     for (auto i : v[nd])        calc(i, nd);}int main(){    scanf("%d", &n);    for (int i = 1; i <= n; i++) {        scanf("%s", str);        int L = strlen(str);        reverse(str, str+L);        push(root, str);    }    fin[root] = 1;    dfs_build(root, 0);    dfs_siz(root);    calc(root, 0);    printf("%lld\n", ans);    return 0;}

loj#2013. 「SCOI2016」幸运数字

小清新数据结构~

原来是用的树剖-线性基三个log不科学做法,现在又写了一个点分治-线性基两个log,果然跑的比谁都快。

#include <bits/stdc++.h>using namespace std;const int MAXN = 20005, MAXQ = 200005;struct linear_base {    long long k[70];    int top = 0;    inline void clear()    { top = 0; }    void push(long long x)    {        for (int i = 1; i <= top; i++)            if ((x^k[i]) < x)                x ^= k[i];        if (!x) return;        for (int i = 1; i <= top; i++)            if ((x^k[i]) < k[i])                k[i] ^= x;        k[++top] = x;        for (int i = top; i > 1 && k[i] > k[i-1]; i--)            swap(k[i], k[i-1]);    }    long long get_max()    {        long long ans = 0;        for (int i = 1; i <= top; i++)            if ((ans^k[i])>ans)                ans ^= k[i];        return ans;    }    friend linear_base operator + (const linear_base &a, long long b)    {        linear_base c = a;        c.push(b);        return c;    }    friend linear_base operator + (const linear_base &a, const linear_base &b)    {        linear_base c = a;        for (int i = 1; i <= b.top; i++)            c.push(b.k[i]);        return c;    }};struct node {    int to, next;} edge[MAXN*2];int head[MAXN], top = 0;void push(int i, int j){ edge[++top] = (node) {j, head[i]}, head[i] = top; }int vis[MAXN], siz[MAXN], col[MAXN];long long ans[MAXN*10];long long d[MAXN];linear_base lb[MAXN];int n, q;void dfs_siz(int nd, int f){    siz[nd] = 1;    for (int i = head[nd]; i; i = edge[i].next) {        int to = edge[i].to;        if (to == f || vis[to]) continue;        dfs_siz(to, nd), siz[nd] += siz[to];    }}void dfs_find_center(int nd, int f, const int totsiz, int &ans, int &max_siz){    int cnt = (f!=0)*(totsiz-siz[nd]);    for (int i = head[nd]; i; i = edge[i].next) {        int to = edge[i].to;        if (to == f || vis[to]) continue;        dfs_find_center(to, nd, totsiz, ans, max_siz);        cnt = max(cnt, siz[to]);    }    if (cnt < max_siz) ans = nd, max_siz = cnt;}void dfs_paint(int nd, int f, int c){    col[nd] = c;        lb[nd] = lb[f]+d[nd];    for (int i = head[nd]; i; i = edge[i].next) {        int to = edge[i].to;        if (to == f || vis[to]) continue;        dfs_paint(to, nd, c);    }}vector<pair<int, int> > qy[MAXN];int tp = 0;int tmp = 0;void dfs_calc(int nd, int f){    for (auto i : qy[nd])         if (col[i.first] > tmp && col[i.first] < col[nd])             ans[i.second] = (lb[i.first]+lb[nd]).get_max();    for (int i = head[nd]; i; i = edge[i].next) {        int to = edge[i].to;        if (to == f || vis[to]) continue;        dfs_calc(to, nd);    }}void calc(int nd){    dfs_siz(nd, 0);    int center = nd, tt = INT_MAX;    dfs_find_center(nd, 0, siz[nd], center, tt), vis[center] = 1;        tmp = tp;    lb[center].clear(), lb[center].push(d[center]);    // cerr << center << endl;    for (int i = head[center]; i; i = edge[i].next) {        int to = edge[i].to;        if (vis[to]) continue;        dfs_paint(to, center, ++tp);    }    for (auto i : qy[center])         if (col[i.first] > tmp) {            ans[i.second] = lb[i.first].get_max();        }    for (int i = head[center]; i; i = edge[i].next) {        int to = edge[i].to;        dfs_calc(to, center);    }    for (int i = head[center]; i; i = edge[i].next) {        int to = edge[i].to;        if (!vis[to]) calc(to);    }}int main(){    scanf("%d%d", &n, &q);    for (int i = 1; i <= n; i++) scanf("%lld", &d[i]);    for (int i = 1; i < n; i++) {        int u, v; scanf("%d%d", &u, &v);        push(u, v), push(v, u);    }    for (int i = 1; i <= q; i++) {        int x, y; scanf("%d%d", &x, &y);        if (x == y) ans[i] = d[x];        else qy[x].push_back(make_pair(y, i)), qy[y].push_back(make_pair(x, i));    }    calc(1);    for (int i = 1; i <= q; i++)        printf("%lld\n", ans[i]);    return 0;}

loj#2014. 「SCOI2016」萌萌哒

真-神数据结构系列…..

和花神游历各国是一类题…本质有用的操作只有很少(这道题是O(n)的),但有很多的无效操作,关键在于如何将无效操作最小化。

这个题的方法是你用ST表的思路,将l,r拆成二的整次幂,用f[n][i]n开始i个人的一个大点。同层只和同层的连边,表示是否整个都连接上了。对于每一次操作,如果同层已经连过边了,那么退出;否则说明还有空位置,这样在同层连边,并递归的连儿子。由于一共只有n个有用操作,每次有用操作的代价是O(lgn)次并查集操作,无用操作不会超过O(nlgn)个,总复杂度是O(nlgn)的。如果用标记实现常数会优越一点。

#include <bits/stdc++.h>using namespace std;const int MAXN = 100005;int n, q;pair<int,int> f[MAXN][20];pair<int,int> findf(const pair<int,int> &pr){ return f[pr.first][pr.second]!=pr?f[pr.first][pr.second]=findf(f[pr.first][pr.second]):pr; }const int mod = 1e9+7;void link(const pair<int,int> &a, const pair<int,int> &b){    // cerr << a.first << " " << a.second << "," << b.first << " " << b.second << endl;    pair<int,int> fa = findf(a), fb = findf(b);    if (fa == fb) return;    f[fa.first][fa.second] = fb;    if (a.second != 0) {        link(make_pair(a.first, a.second-1), make_pair(b.first, b.second-1));        link(make_pair(a.first+(1<<(a.second-1)), a.second-1), make_pair(b.first+(1<<(b.second-1)), b.second-1));    }}int main(){    scanf("%d%d", &n, &q);    int l1, l2, r1, r2;    for (int i = 1; i <= n; i++)        for (int j = 0; j < 20; j++)            f[i][j] = make_pair(i, j);    for (int i = 1; i <= q; i++) {        scanf("%d%d%d%d", &l1, &r1, &l2, &r2);        int d = r1-l1+1;        for (register int j = 0; j < 20; j++)            if (d&(1<<j)) {                link(make_pair(l1, j), make_pair(l2, j));                l1 += (1<<j), l2 += (1<<j);            }    }    int ans = 1;    for (int i = 1; i <= n; i++)        if (f[i][0] == make_pair(i, 0)) {            if (findf(make_pair(1, 0)) == f[i][0]) ans = (long long)ans*9%mod;            else ans = (long long)ans*10%mod;        }    printf("%d\n", ans);    return 0;}

loj#2015. 「SCOI2016」妖怪

一眼题…设a/b=λ,先二分个答案,然后发现每一个妖怪λ可行取值都在一个区间内,就做了。

但是这样会被卡时间…然后发现有几个数据都是递增的,可能到最后才能发现冲突…random_shullfe了一下就过了…以后要养成random_shuffle的好习惯…

大家貌似是三分的…?按照codeforces那个题的经验三分可能有点问题吧..

#include <bits/stdc++.h>using namespace std;const int MAXN = 1000005;int n;struct p {    double a, b;    friend bool operator < (const p &a, const p &b)    { return a.a<b.a||(a.a==b.a&&a.b<b.b); }} pt[MAXN];double L = 0, R = 1e10;bool judge(double k){    L = 0, R = 1e10;    register double w, dt;    for (register int i = 1; i <= n; i++) {        w = k-pt[i].a-pt[i].b, dt = sqrt(w*w-4*pt[i].a*pt[i].b);        L = max((w-dt)/(2*pt[i].b), L), R = min((w+dt)/(2*pt[i].b), R);        if (L >= R) return 0;    }    return 1;}int main(){    scanf("%d", &n);    for (int i = 1; i <= n; i++)        scanf("%lf%lf", &pt[i].a, &pt[i].b);    //sort(pt+1, pt+n+1);    srand(time(0));    random_shuffle(pt+1, pt+n+1);    double l = 0, r = 1.5e8, mid;    for (int i = 1; i <= n; i++)        l = max(l, pt[i].a+pt[i].b+2*sqrt(pt[i].a*pt[i].b));        while (r-l >= 1e-6) {        mid = (l+r)/2;        if (judge(mid)) r = mid;        else l = mid;    }    printf("%.4f\n", l);    return 0;}

loj#2016. 「SCOI2016」美味

新套路get√…

其实那个异或只不过是改变了每一位0/1的优先级而已..所以按照新的优先级二分(其实就是逐位确定)然后用主席树统计一下答案…

#include <bits/stdc++.h>using namespace std;const int MAXN = 200005;int n, m;int a[MAXN];int sum[MAXN*40], l[MAXN*40], r[MAXN*40], lc[MAXN*40], rc[MAXN*40], top = 0, root[MAXN];void build(int &nd, int opl, int opr){    nd = ++top, l[nd] = opl, r[nd] = opr;    if (opl < opr) build(lc[nd], opl, (opl+opr)/2), build(rc[nd], (opl+opr)/2+1, opr);}void modify(int pre, int &nd, int pos, int dt){    nd = ++top, l[nd] = l[pre], r[nd] = r[pre], sum[nd] = sum[pre]+dt;    if (l[nd] < r[nd]) {        int mid = (l[nd]+r[nd])/2;        if (pos <= mid) rc[nd] = rc[pre], modify(lc[pre], lc[nd], pos, dt);        else lc[nd] = lc[pre], modify(rc[pre], rc[nd], pos, dt);    }}int query(int nd, int opl, int opr){    // cerr << opl << " " << opr << " " << l[nd] << " " << r[nd] << endl;    if (opl > opr) return 0;    if (l[nd] == opl && r[nd] == opr) return sum[nd];    else {        int mid = (l[nd]+r[nd])/2;        if (opr <= mid) return query(lc[nd], opl, opr);        else if (opl > mid) return query(rc[nd], opl, opr);        else return query(lc[nd], opl, mid)+query(rc[nd], mid+1, opr);    }}int query_sum(int L, int R, int opl, int opr){    // cerr << opl << " " << opr << endl;    return query(root[R], opl, opr)-query(root[L-1], opl, opr);}int main(){    scanf("%d%d", &n, &m);    for (int i = 1; i <= n; i++) scanf("%d", &a[i]);    build(root[0], 1, n);    for (int i = 1; i <= n; i++) modify(root[i-1], root[i], a[i], 1);    cerr << query_sum(1, 3, 2, 4) << endl;    for (int i = 1; i <= m; i++) {        int b, x, opl, opr, cur = 0;        scanf("%d%d%d%d", &b, &x, &opl, &opr);        for (int j = 20; j >= 0; j--) {            int pos = ((b&(1<<j))==0);            if (query_sum(opl, opr, max(1, (cur|(pos<<j))-x), min((cur|(pos<<j)|((1<<j)-1))-x, n)) > 0) {                cur |= pos<<j;            } else cur |= (!pos)<<j;        }        printf("%d\n", cur^b);    }    return 0;}

loj#2017. 「SCOI2016」围棋

轮廓线dp….坑待填