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
)表示原问题的答案,则有
进而得到
除去求u函数,直接求复杂度是 O(ai∗n) 的。由于 aid 的值只有 ai−−√ 个,所以在计算时可以根据 aid 的值分段计算,从而将复杂度降低到 O(ai+nai−−√ )
u函数的求法:
由于空间只给了 65M ,所以想直接拿数组存是不行的,由于u函数只有3个值,于是想到用2个bitset来存u函数,1个bitset来存质数标记,这里用去了 3e8/8≈37.5M ,线性筛还需要一个数组来存质数表,需要 4∗1e8/17≈23.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;
}