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

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


loj#2012. 「SCOI2016」背单词

神贪心…

所有串翻转,考虑建一棵这样的树, ij 当且仅当 si sj 最长的前缀。这棵树可以用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….坑待填

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值