vijos训练之——星辰大海中闪烁的趣题

看到难度普遍7-9就感觉很劲…再加上doc出品,于是果断入坑。由于知识性内容还没有学完,只能慢慢刷了。

章节 1. 一些简单的练手题

1.1 捕风捉影

这sb题居然智障WA了两次….

由于是回文串只要枚举前半段,然后瞎几把测试素数就好了。
顺便学习一波miller-rabin的二次探查。

复杂度 O(nSlgn) ,S为测试次数。

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

long long power(long long a, long long n, long long mod)
{
    if (n == 0) return 1%mod;
    long long p = power(a, n>>1, mod);
    p = p*p%mod;
    if (n&1) p = p*a%mod;
    return p;
}

bool miller_rabin(long long n)
{
    if (n == 2) return 1;
    if (n < 2 || !(n&1)) return 0;
    int k;
    long long u;
    for (k=0, u=n-1; !(u&1); u>>=1, k++);
    for (int i = 1; i < 20; i++) {
        long long x = rand()%(n-2)+2, pre;
        if (x%n == 0) continue;
        x = power(x, u, n);
        pre = x;
        for (int j = 1; j <= k; j++) {
            x = x*x%n;
            if (x == 1 && pre != 1 && pre != n-1) return 0;
            pre = x;
        }
        if (x != 1) return 0;
    }
    return 1;
}

long long rev(long long a)
{
    long long t = 0;
    while (a) t = (t+a%10)*10, a /= 10;
    return t/10;
}
long long ans[100005];
int top = 0;
int main()
{
    srand(time(0));
    long long n, m;
    cin >> n >> m;
    if (5 >= n && 5 <= m) ans[++top] = 5;
    if (7 >= n && 7 <= m) ans[++top] = 7;
    int len = 1;
    for (long long i = 1; i <= 10000; i++) {
        long long l = i, r = rev(i);
        long long lk = l*power(10, len, LONG_LONG_MAX)+r;
        if (lk >= n && lk <= m && miller_rabin(lk)) ans[++top] = lk;
        for (int j = 0; j < 10; j++) {
            lk = l*power(10, len+1, LONG_LONG_MAX)+j*power(10, len, LONG_LONG_MAX)+r;
            if (lk >= n && lk <= m && miller_rabin(lk)) ans[++top] = lk;
        }
        if (i >= power(10, len, LONG_LONG_MAX)-1) len++;
    }
    sort(ans+1, ans+top+1);
    top = unique(ans+1, ans+top+1)-ans-1;
    for (int i = 1; i <= top; i++) printf("%lld\n", ans[i]);
    return 0;
}

1.2 小白逛公园

以前写线段树查询从来都是直接返回答案…做了这题才发现可以返回一个完整的节点信息。

就是线段树维护总和,最大子段和,左起最大子段和,右起最大子段和。

复杂度 O(nlgn)

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

const int MAXN = (1<<19)+2;
struct node {
    int lsum, rsum, sum, subsum, l, r;
    node():l(0),r(0){}
    friend bool operator == (const node &a, const node &b)
    {
        return a.l == b.l;
    }
} tree[MAXN*2];
int a[MAXN], n, m;
#define nil tree[0]

node link(const node &l, const node &r)
{
    if (l == nil) return r;
    if (r == nil) return l;
    node nd;
    nd.sum = l.sum + r.sum;
    nd.lsum = max(l.lsum, l.sum+r.lsum);
    nd.rsum = max(r.rsum, r.sum+l.rsum);
    nd.subsum = max(l.subsum, max(r.subsum, l.rsum+r.lsum));
    return nd;
}

void build(int nd, int l, int r)
{
    if (l < r) {
        build(nd*2, l, (l+r)/2);
        build(nd*2+1, (l+r)/2+1, r);
        tree[nd] = link(tree[nd*2], tree[nd*2+1]);
        tree[nd].l = l, tree[nd].r = r;
    } else {
        tree[nd].l = tree[nd].r = l;
        tree[nd].sum = tree[nd].lsum = tree[nd].rsum = tree[nd].subsum = a[l];
    }
}

void dfs(int nd, int tab = 0)
{
    if (!tree[nd].l) return;
    for (int i = 1; i <= tab; i++) putchar(' ');
    //printf("[%d,%d] : %d,%d,%d,%d\n", tree[nd].l, tree[nd].r, tree[nd].lsum, tree[nd].rsum, tree[nd].subsum, tree[nd].sum);
    dfs(nd*2, tab+2);
    dfs(nd*2+1, tab+2);
}

void update(int nd, int pos, int dat)
{
    if (tree[nd].l == tree[nd].r) {
        tree[nd].sum = tree[nd].lsum = tree[nd].rsum = tree[nd].subsum = dat;
    } else {
        int l = tree[nd].l, r = tree[nd].r;
        update((pos<=(l+r)/2)?nd*2:nd*2+1, pos, dat);
        tree[nd] = link(tree[nd*2], tree[nd*2+1]);
        tree[nd].l = l, tree[nd].r = r;
    }
}

node query(int nd, int l, int r)
{
    if (l > r) return nil;
    if (tree[nd].l == l && tree[nd].r == r) return tree[nd];
    node d = link(query(nd*2, l, min(r, tree[nd*2].r)),
                  query(nd*2+1, max(l, tree[nd*2+1].l), r));
    //printf("[%d,%d] %d %d->%d,%d,%d\n", tree[nd].l, tree[nd].r, l, r, d.subsum, d.lsum,d .rsum);
    return d;
}

int main()
{
    scanf("%d%d", &n, &m);
    tree[0].l = 123231131;
    for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
    build(1, 1, n);
    for (int i = 1; i <= m; i++) {
        int opt, opl, opr;
        scanf("%d%d%d", &opt, &opl, &opr);
        if (opt == 1) {
            if (opl > opr) swap(opl, opr);
            printf("%d\n",query(1, opl, opr).subsum);
        } else if (opt == 2) {
            update(1, opl, opr);
        } else dfs(1);
    }
    return 0;
}

1.3 CoVH之柯南开锁

一开始以为是反转思考了好久,后来才发现只是按下去。

最小割经典建图:

  1. Srow
  2. colT
  3. rowcol,if there is something at (row,col)

正确性是显然的,复杂度 O()

貌似用二分图匹配也可以?但我不会匈牙利那一套理论。

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

const int MAXN = 50005, MAXM = 5000005;
struct node {
    int to, next, f, neg;
} edge[MAXM];
int head[MAXN], top = 0;
void push(int i, int j, int k)
{
    ++top, edge[top] = (node) {j, head[i], k, top+1}, head[i] = top;
    ++top, edge[top] = (node) {i, head[j], 0, top-1}, head[j] = top;
}

int vis[MAXN], bfstime = 0;
int lev[MAXN], S = 50001, T = 50002;
queue<int> que;

bool bfs()
{
    vis[S] = ++bfstime, lev[S] = 0, que.push(S);
    while (!que.empty()) {
        int tp = que.front(); que.pop();
        for (int i = head[tp]; i; i = edge[i].next) {
            if (edge[i].f == 0 || vis[edge[i].to] == bfstime) continue;
            vis[edge[i].to] = bfstime, lev[edge[i].to] = lev[tp]+1, que.push(edge[i].to);
        }
    } 
    return vis[T] == bfstime;
}

int dfs(int nd, int maxf = 233333333)
{
    if (nd == T || !maxf) return maxf;
    int ans = 0, t;
    for (int i = head[nd]; i; i = edge[i].next) {
        if (edge[i].f == 0 || lev[edge[i].to] != lev[nd] + 1) continue;
        t = dfs(edge[i].to, min(maxf, edge[i].f));
        ans += t, maxf -= t;
        edge[i].f -= t, edge[edge[i].neg].f += t;
    } 
    if (maxf) lev[nd] = -1;
    return ans;
}

int dinic()
{
    int ans = 0;
    while (bfs()) ans += dfs(S);
    return ans;
}

int n, m;
char str[105];
int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++) push(S, i, 1);
    for (int i = 1; i <= m; i++) push(n+i, T, 1);
    for (int i = 1; i <= n; i++) {
        scanf("%s", str+1);
        for (int j = 1; j <= m; j++) 
            if (str[j] == '1')
                push(i, n+j, 1);
    }
    cout << dinic() << endl;
    return 0;
}

章节 2. 轻松点它们很简单

2.1 cpc的逃离

变得难起来了…
构造问题,由于有二叉树计数考虑用卡特兰数。然后瞎几把搞【雾】。
复杂度 O(lgn) 。因为卡特兰数是指数增长,其反函数是对数增长。

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

// C : h(n)=h(n-1)*(4*n-2)/(n+1);

long long h[20];

void work(int n, long long k, bool out_side = 1) // n个节点中编号为k的 
{
    //cerr << endl << n << " " << k << endl;
    if (k*n == 0) return;
    if (!out_side) putchar('(');
    for (int i = 0; i < n; i++) {
        if (h[i]*h[n-i-1] < k) k -= h[i]*h[n-i-1];
        else {
            work(i, (k-1)/h[n-i-1]+1, 0);
            putchar('X');
            work(n-i-1, k%h[n-i-1]?k%h[n-i-1]:h[n-i-1], 0);
            break;
        }
    }
    if (!out_side) putchar(')');
}

int main()
{
    h[0] = 1;
    for (int i = 1; i < 20; i++) h[i] = h[i-1]*(4*i-2)/(i+1);//, printf("%lld\n", h[i]);
    long long x;
    while (scanf("%lld", &x), x != 0) {
        int i = 1;
        for (; x > h[i]; x -= h[i], i++);
        work(i, x);
        puts("");
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值