“高教社杯”第三届福建省大学生程序设计竞赛

FZU OJ主页:http://acm.fzu.edu.cn/index.php

Problem B. Bin & Jing in wonderland @FZU2103
题意

1-n共n个id,每个id给定一个概率,表示从中随机抽一个抽到它的概率。现在让你有放回地随机抽取k次,把这k个id排序后最大的r个id与给定的r个id一样的概率。

思路

根据题意,有r个id是确定的,另外 k-r 个id可以这样确定,把它统一看成新的id,出现概率等于“小于等于最小id的所有id的概率和”,因为所有小于等于最小id的id在 k-r 个里面是没有区别的。于是将给定的id排序,统计每一种id的个数,然后按id从大到小处理,问题转化为“在m个位置填cnt个相同数字有几种填法”,组合数学搞搞就出来了。

source
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <stack>
using namespace std;
double Mul(double p, int c) {
    double ans = 1;
    while (c--) {
        ans *= p;
    }
    return ans;
}
double C[55][55], p[22];
int li[55], cnt[55], _, n, k, r;
int main() {
    C[0][0] = 1;
    for (int i = 1; i <= 52; i++) {
        C[i][0] = C[i][i] = 1;
        for (int j = 1; j < i; j++) {
            C[i][j] = C[i - 1][j] + C[i - 1][j - 1];
        }
    }
    cin >> _;
    while (_--) {
        cin >> n >> k >> r;
        for (int i = 1; i <= n; i++) {
            cin >> p[i];
        }
        memset(cnt, 0, sizeof(cnt));
        for (int i = 1; i <= r; i++) {
            cin >> li[i];
            cnt[li[i]]++;
        }
        sort(li + 1, li + 1 + r);
        double ans = 0, buf = 1;
        for (int i = n; i > li[1]; i--) {
            buf *= C[k][cnt[i]] * Mul(p[i], cnt[i]);
            k -= cnt[i];
        }
        double sum = 0;
        for (int i = 1; i < li[1]; i++) {
            sum += p[i];
        }
        for (int i = cnt[li[1]]; i <= k; i++) {
            ans += buf * C[k][i] * Mul(p[li[1]], i) * Mul(sum, k - i);
        }
        printf("%.6f\n", ans);
    }
    return 0;
}

Problem D. Digits Count @FZU2105
题意

支持区间按位and,按位or,按位xor,区间求和

思路

每个bit单独处理,就可以上线段树了,关键是要处理好标记下放的维护,写熟练了难度不大。

source
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1
#define dm int m = l + r >> 1
const int N = 1e6 + 7;
int a[N];
struct SegTree {
    int bit;
    int sum[N << 2];
    bool mand[N << 2], mor[N << 2], mxor[N << 2];
    void pushup(int rt) {
        sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];
    }
    void pushdown(int rt, int Len) {
        int rLen = Len >> 1, lLen = Len - rLen;
        if (mxor[rt]) {
            sum[rt << 1] = lLen - sum[rt << 1];
            sum[rt << 1 | 1] = rLen - sum[rt << 1 | 1];
            if (mand[rt << 1]) {
                mand[rt << 1] = 0;
                mor[rt << 1] = 1;
            }
            else {
                if (mor[rt << 1]) {
                    mand[rt << 1] = 1;
                    mor[rt << 1] = 0;
                }
                else {
                    mxor[rt << 1] ^= 1;
                }
            }
            if (mand[rt << 1 | 1]) {
                mand[rt << 1 | 1] = 0;
                mor[rt << 1 | 1] = 1;
            }
            else {
                if (mor[rt << 1 | 1]) {
                    mand[rt << 1 | 1] = 1;
                    mor[rt << 1 | 1] = 0;
                }
                else {
                    mxor[rt << 1 | 1] ^= 1;
                }
            }
            mxor[rt] = 0;
        }
        if (mand[rt]) {
            sum[rt << 1] = sum[rt << 1 | 1] = 0;
            mand[rt << 1] = mand[rt << 1 | 1] = 1;
            mor[rt << 1] = mor[rt << 1 | 1] = 0;
            mxor[rt << 1] = mxor[rt << 1 | 1] = 0;
            mand[rt] = 0;
        }
        if (mor[rt]) {
            sum[rt << 1] = lLen;
            sum[rt << 1 | 1] = rLen;
            mor[rt << 1] = mor[rt << 1 | 1] = 1;
            mand[rt << 1] = mand[rt << 1 | 1] = 0;
            mxor[rt << 1] = mxor[rt << 1 | 1] = 0;
            mor[rt] = 0;
        }
    }
    void build(int l, int r, int rt) {
        mand[rt] = mor[rt] = mxor[rt] = 0;
        if (l == r) {
            sum[rt] = (a[l] >> bit) & 1;
            return;
        }
        dm;
        build(lson);
        build(rson);
        pushup(rt);
    }
    void update_and(int L, int R, int l, int r, int rt) {
        if (L <= l && r <= R) {
            sum[rt] = 0;
            mor[rt] = mxor[rt] = 0;
            mand[rt] = 1;
            return;
        }
        pushdown(rt, r - l + 1);
        dm;
        if (L <= m) update_and(L, R, lson);
        if (R > m) update_and(L, R, rson);
        pushup(rt);
    }
    void update_or(int L, int R, int l, int r, int rt) {
        if (L <= l && r <= R) {
            sum[rt] = r - l + 1;
            mand[rt] = mxor[rt] = 0;
            mor[rt] = 1;
            return;
        }
        pushdown(rt, r - l + 1);
        dm;
        if (L <= m) update_or(L, R, lson);
        if (R > m) update_or(L, R, rson);
        pushup(rt);
    }
    void update_xor(int L, int R, int l, int r, int rt) {
        if (L <= l && r <= R) {
            sum[rt] = r - l + 1 - sum[rt];
            mxor[rt] ^= 1;
            if (mxor[rt] && mand[rt]) {
                mand[rt] = 0;
                mor[rt] = 1;
                mxor[rt] = 0;
            }
            else {
                if (mxor[rt] && mor[rt]) {
                    mand[rt] = 1;
                    mor[rt] = 0;
                    mxor[rt] = 0;
                }
            }
            return;
        }
        pushdown(rt, r - l + 1);
        dm;
        if (L <= m) update_xor(L, R, lson);
        if (R > m) update_xor(L, R, rson);
        pushup(rt);
    }
    int query(int L, int R, int l, int r, int rt) {
        if (L <= l && r <= R) {
            return sum[rt];
        }
        pushdown(rt, r - l + 1);
        dm;
        int ans = 0;
        if (L <= m) ans += query(L, R, lson);
        if (R > m) ans += query(L, R, rson);
        return ans;
    }
} st[4];
int _, n, m, l, r, x;
char s[22];
int getAns(int l, int r) {
    int ans = 0;
    for (int i = 3; i >= 0; i--) {
        ans = ans * 2 + st[i].query(l, r, 0, n - 1, 1);
    }
    return ans;
}
int main() {
    cin >> _;
    while (_--) {
        cin >> n >> m;
        for (int i = 0; i < 4; i++) st[i].bit = i;
        for (int i = 0; i < n; i++) {
            scanf("%d", a + i);
        }
        for (int i = 0; i < 4; i++) st[i].build(0, n - 1, 1);
        while (m--) {
            scanf("%s", s);
            if (s[0] == 'S') {
                scanf("%d%d", &l, &r);
                printf("%d\n", getAns(l, r));
            }
            if (s[0] == 'A') {
                scanf("%d%d%d", &x, &l, &r);
                for (int i = 0; i < 4; i++) {
                    if (!((x >> i) & 1)) {
                        st[i].update_and(l, r, 0, n - 1, 1);
                    }
                }
            }
            if (s[0] == 'O') {
                scanf("%d%d%d", &x, &l, &r);
                for (int i = 0; i < 4; i++) {
                    if ((x >> i) & 1) {
                        st[i].update_or(l, r, 0, n - 1, 1);
                    }
                }
            }
            if (s[0] == 'X') {
                scanf("%d%d%d", &x, &l, &r);
                for (int i = 0; i < 4; i++) {
                    if ((x >> i) & 1) {
                        st[i].update_xor(l, r, 0, n - 1, 1);
                    }
                }
            }
        }
    }
    return 0;
}

Problem E. How many tuples @FZU2106
题意

求有多少个序列 b1,b2,...,bn 满足gcd( b1,b2,...,bn )=1,其中 1<=bi<=ai

思路

令f( a1,a2,...,an )表示原问题的答案,则有

i=1nai=d>=1f(a1/d,a2/d,...,an/d)

进而得到
f(a1,a2,...,an)=d>=1u(d)i=1naid

除去求u函数,直接求复杂度是 O(ain) 的。由于 aid 的值只有 ai 个,所以在计算时可以根据 aid 的值分段计算,从而将复杂度降低到 O(ai+nai )
u函数的求法:
由于空间只给了 65M ,所以想直接拿数组存是不行的,由于u函数只有3个值,于是想到用2个bitset来存u函数,1个bitset来存质数标记,这里用去了 3e8/837.5M ,线性筛还需要一个数组来存质数表,需要 41e8/1723.5M ,共约 61M ,卡过。
最后在OJ上10s的时限跑了8s,80%的空间+80%的时间 也算是充分地利用了服务器的时空资源了-_-||

source
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <stack>
#include <cctype>
#include <bitset>
#include <vector>
using namespace std;
const int N = 1e8 + 7;
const int mod = 1e9 + 7;
struct BitSet {
    char bit[N / 8 + 7];
    void setBit(int p, bool x) {
        if (x) bit[p >> 3] |= 1 << (p & 7);
        else bit[p >> 3] &= ~(1 << (p & 7));
    }
    bool operator[] (int p) {
        return bit[p >> 3] & (1 << (p & 7));
    }
} miu, iszero, noprime;
int prime[N / 17], nprime;
int _, a[22], m;
void init() {
    miu.setBit(1, 1);
    for (int i = 2; i < N; i++) {
        if (!noprime[i]) prime[nprime++] = i;
        for (int j = 0; j < nprime && prime[j] * i < N; j++) {
            noprime.setBit(i * prime[j], 1);
            if (i % prime[j] == 0) {
                iszero.setBit(i * prime[j], 1);
                break;
            }
            miu.setBit(i * prime[j],  miu[i] ^ 1);
            if (iszero[i]) iszero.setBit(i * prime[j], 1);
        }
    }
}
int getAns(int g) {
    long long ans = 1;
    for (int i = 1; i <= m; i++) {
        ans = ans * (a[i] / g) % mod;
    }
    return ans;
}
int getNext(int start) {
    int ans = 1e9 + 7;
    for (int i = 1; i <= m; i++) {
        ans = min(ans, a[i] / (a[i] / start));
    }
    return ans;
}
int main() {
    init();
    cin >> _;
    while (_--) {
        scanf("%d", &m);
        int maxg = 1e9 + 7;
        for (int i = 1; i <= m; i++) {
            scanf("%d", a + i);
            maxg = min(maxg, a[i]);
        }
        int ans = 0;
        for (int i = 1, j; i <= maxg; i = j + 1) {
            j = getNext(i);
            long long sum = 0;
            for (int t = i; t <= j; t++) {
                if (!iszero[t]) {
                    if (miu[t]) sum++;
                    else sum--;
                }
            }
            sum = (sum + mod) % mod;
            int buf = getAns(i);
            ans = (ans + sum * buf) % mod;
        }
        printf("%d\n", ans);
    }
    return 0;
}

Problem G. Mod problem @FZU2108
题意

定义一种数的递归压缩方式,让你解压一个压缩了的数,求它取模mod的值

思路

递归定义的东西通常用递归来解决,由于解压方式唯一,根据题意,先用栈将括号匹配下,对于一个序列s:
1.如果s长度为1,那么直接返回数值
2.如果是[s’]d的形式,那么先算s’,算完后再算整体
3.如果1.和2.都不满足那么说明s肯定可分成独立的两部分,再看s的倒数第二个字符是不是’]’,如果是,就找到它对应的’[‘的位置,从此处将s断开成两部分,分别求出值后在合并成整体;如果不是则在s的最后一个字符处断开成两部分,剩下的同前面。

source
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <stack>
using namespace std;
typedef long long ll;
ll B;
int last[1234];
char s[1234];
struct Node {
    ll ans, len;
    Node(ll a = 0, ll b = 0) {
        ans = a;
        len = b;
    }
};
ll powermod(ll a, ll n, ll mod) {
    ll ans = 1;
    while (n) {
        if (n & 1) ans = ans * a % mod;
        a = a * a % mod;
        n >>= 1;
    }
    return ans;
}
ll getAns(ll a, ll len, int x) {
    ll ans = 0;
    for (int i = x - 1; i >= 0; i--) {
        ans = (ans + powermod(10, len * i, B) * a % B) % B;
    }
    return ans;
}
Node dfs(int l, int r) {
    if (l > r) return Node(0, 0);
    if (l == r) return Node((s[l] - '0') % B, 1);
    if (s[l] == '[' && s[r - 1] == ']' && last[r - 1] == l) {
        Node dn = dfs(l + 1, r - 2);
        return Node(getAns(dn.ans, dn.len, s[r] - '0'), dn.len * (s[r] - '0'));
    }
    if (s[r - 1] != ']') {
        Node dn = dfs(l, r - 1);
        return Node((dn.ans * 10 + s[r] - '0') % B, dn.len + 1);
    }
    else {
        Node dl = dfs(l, last[r - 1] - 1), dr = dfs(last[r - 1], r);
        return Node((dl.ans * powermod(10, dr.len, B) % B + dr.ans) % B, dl.len + dr.len);
    }
}
int _, n;
stack<int> stk;

int main() {
    cin >> _;
    while (_--) {
        scanf("%s", s);
        cin >> B;
        n = strlen(s);
        if (B == 1) {
            puts("0");
            continue;
        }
        for (int i = 0; i < n; i++) {
            if (s[i] == '[') {
                stk.push(i);
            }
            if (s[i] == ']') {
                last[i] = stk.top();
                stk.pop();
            }
        }
        cout << dfs(0, n - 1).ans << endl;
    }
    return 0;
}

Problem G. Mountain Number @FZU2109
题意

定义山峰数为从左边第1位开始,偶数位的数字大于等于它的两个相邻位的数字(如果有的话),求L~R有多少个山峰数

思路

根据题意,山峰数从左边第1位开始,依次为“上升、下降、上升、下降”,所以必须保存当前的山峰数的末尾是上升还是下降状态,否则无法转移。对于一个山峰数,它的前缀也必定是山峰数(废话-_-||),所以长的山峰数只能由短的山峰数来构造。于是令dp[n][x][s]表示长度n,末尾是x,上升状态为s的山峰数数目,dp数组很容易计算出来。
求L-R之间的山峰数目可以用f(R+1)-f(l)来表示,其中f(i)表示小于i的山峰数个数。求f(i)可以从i的高位到低位逐位计算,根据和i的“最长公共前缀”长度来计算,逐步递推到低位即可。

source
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
int dp[11][11][2];
void init() {
    for (int i = 0; i < 10; i++) dp[0][i][0] = dp[0][i][1] = 1;
    for (int i = 1; i <= 10; i++) {
        for (int j = 0; j < 10; j++) {
            for (int k = 0; k <= j; k++) {
                dp[i][j][0] += dp[i - 1][k][1];
            }
            for (int k = j; k < 10; k++) {
                dp[i][j][1] += dp[i - 1][k][0];
            }
        }
    }
}
int getCnt(int n, int minv, int maxv, int t) {
    if (n == 0) return minv <= maxv;
    int ans = 0;
    for (int i = minv; i <= maxv; i++) {
        ans += dp[n - 1][i][t];
    }
    return ans;
}

int calc(int x) {
    int div[11], dc = 0;
    do {
        div[dc++] = x % 10;
        x /= 10;
    } while (x);
    int ans = 0;
    for (int i = 1; i < dc; i++) {
        ans += getCnt(i, 1, 9, 1);
    }
    int t = 0, minv = 1, lastv = 9;
    for (int i = dc - 1; i >= 0; i--) {
        if (t == 0) {
            for (int j = minv; j <= min(lastv, div[i] - 1); j++) {
                ans += getCnt(i, j, 9, 0);
            }
        }
        else {
            for (int j = lastv; j < div[i]; j++) {
                ans += getCnt(i, 0, j, 1);
            }
        }
        if (t == 0 && lastv < div[i] || t == 1 && lastv > div[i]) break;
        minv = 0;
        t ^= 1;
        lastv = div[i];
    }
    return ans;
}
int main() {
    init();
    int _, l, r;
    cin >> _;
    while (_--) {
        cin >> l >> r;
        cout << calc(r + 1) - calc(l) << endl;
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值