[Codeforces] combinatorics (R1600) Part.4

[Codeforces] combinatorics (R1600) Part.4

题单:https://codeforces.com/problemset?tags=combinatorics,1201-1600

735C. Tennis Championship

原题指路:https://codeforces.com/problemset/problem/735/C

题意 ( 2   s 2\ \mathrm{s} 2 s)

求一个包含 n    ( 2 ≤ n ≤ 1 e 18 ) n\ \ (2\leq n\leq 1\mathrm{e}18) n  (2n1e18)个节点的、每个节点的两棵子树高度之差不超过 1 1 1的二叉树的最大高度(高度从 0 0 0开始).

思路

f ( h ) f(h) f(h)表示高度为 h h h的满足条件的二叉树所包含的最小节点数.考虑其根节点的两棵子树,它们的高度只能为 ( h − 1 ) (h-1) (h1) ( h − 2 ) (h-2) (h2),则 f ( h ) = f ( h − 1 ) + f ( h − 2 ) f(h)=f(h-1)+f(h-2) f(h)=f(h1)+f(h2).

S n = ∑ i = 1 n f ( i ) \displaystyle S_n=\sum_{i=1}^n f(i) Sn=i=1nf(i),问题转化为求最小的 a n s   s . t .   S a n s ≥ n ans\ s.t.\ S_{ans}\geq n ans s.t. Sansn.

代码

vl fib, ans;

void init() {
  fib.push_back(1), fib.push_back(1);
  for (int i = 2; fib.back() < 1e18; i++)
    fib.push_back(fib[fib.size() - 2] + fib[fib.size() - 1]);

  ans.push_back(0);
  for (int i = 0; ans.back() < 1e18; i++)
    ans.push_back(fib[i] + ans.back());
}

void solve() {
  init();

  ll n; cin >> n;
  cout << lower_bound(all(ans), n) - ans.begin() - 1;
}

int main() {
  solve();
}


804B. Minimum number of steps

原题指路:https://codeforces.com/problemset/problem/804/B

题意

给定一个长度不超过 1 e 6 1\mathrm{e}6 1e6的只包含字符’a’和’b’的字符串 s s s.现有操作:选择 s s s中的一个子串"ab",将其替换为"bba",无法操作时停止.求最小操作次数,答案对 1 e 9 + 7 1\mathrm{e}9+7 1e9+7取模.

思路

注意到操作一次会减少一对’a’在’b’之前的字符对,同时使得’b’的个数翻倍,则最终字符串形如 b ⋯ b a ⋯ a b\cdots ba\cdots a bbaa.

注意到前面的子串"ab"操作一次后产生的’a’可能会与后面的’b’构成子串"ab",故可从后往前考虑,遇到一个字符’b’时令其 c n t + + cnt++ cnt++;遇到一个字符’a’时 a n s + = c n t , c n t ∗ = 2 ans+=cnt,cnt*=2 ans+=cnt,cnt=2.

代码

const int MOD = 1e9 + 7;

void solve() {
  string s; cin >> s;

  reverse(all(s));
  int cnt = 0;  // 字符'b'的个数
  int ans=0;
  for (auto ch : s) {
    if (ch == 'b') cnt++;
    else {
      ans = ((ll)ans + cnt) % MOD;
      cnt = (ll)cnt * 2 % MOD;
    }
  }
  cout << ans;
}

int main() {
  solve();
}


817B. Makes And The Product

原题指路:https://codeforces.com/problemset/problem/817/B

题意 ( 2   s ) (2\ \mathrm{s}) (2 s)

给定一个长度为 n    ( 3 ≤ n ≤ 1 e 5 ) n\ \ (3\leq n\leq 1\mathrm{e}5) n  (3n1e5)的整数序列 a 1 , ⋯   , a n    ( 1 ≤ a i ≤ 1 e 9 ) a_1,\cdots,a_n\ \ (1\leq a_i\leq 1\mathrm{e}9) a1,,an  (1ai1e9).求有多少对 ( i , j , k )   s . t .   i < j < k , a i ⋅ a j ⋅ a k (i,j,k)\ s.t.\ i<j<k,a_i\cdot a_j\cdot a_k (i,j,k) s.t. i<j<k,aiajak最小.

思路

显然应贪心地选 a [ ] a[] a[]中前三小的元素,不妨设为 x , y , z x,y,z x,y,z,它们的出现次数分别为 c n t 1 , c n t 2 , c n t 3 cnt_1,cnt_2,cnt_3 cnt1,cnt2,cnt3.

x ≠ y ≠ z x\neq y\neq z x=y=z时, a n s = c n t 1 ⋅ c n t 2 ⋅ c n t 3 ans=cnt_1\cdot cnt_2\cdot cnt_3 ans=cnt1cnt2cnt3.

x = y = z x=y=z x=y=z时, a n s = C c n t 1 3 ans=C_{cnt_1}^3 ans=Ccnt13.

x = y ≠ z x=y\neq z x=y=z时, a n s = C c n t 1 2 ⋅ c n t 3 ans=C_{cnt_1}^2\cdot cnt_3 ans=Ccnt12cnt3.

x ≠ y = z x\neq y=z x=y=z时, a n s = c n t 1 ⋅ C c n t 2 2 ans=cnt_1\cdot C_{cnt_2}^2 ans=cnt1Ccnt22.

代码

map<int, int> cnt;

ll C(int n, int m) {  // 组合数C(n,m)
  ll res = 1;
  for (int i = n - m + 1; i <= n; i++) res = res * i / (i - n + m);
  return res;
}

void solve() {
  int n; cin >> n;
  vi a(n);
  for (auto& ai : a) {
    cin >> ai;
    cnt[ai]++;
  }

  sort(all(a));
  if (a[0] != a[1] && a[1] != a[2]) cout << (ll)cnt[a[0]] * cnt[a[1]] * cnt[a[2]];
  else if (a[0] == a[1] && a[1] == a[2]) cout << C(cnt[a[0]], 3);
  else if (a[0] == a[1] && a[1] != a[2]) cout << C(cnt[a[0]], 2) * cnt[a[2]];
  else cout << C(cnt[a[1]], 2) * cnt[a[0]];
}

int main() {
    solve();
}


840A. Leha and Function

原题指路:https://codeforces.com/problemset/problem/840/A

题意 ( 2   s ) (2\ \mathrm{s}) (2 s)

考察从 n n n个元素 [ 1 , ⋯   , n ] [1,\cdots,n] [1,,n]中选 k k k个元素构成一个子集,定义 f ( n , k ) f(n,k) f(n,k)表示所有子集中最小元素的期望.

给定两长度为 n    ( 1 ≤ n ≤ 2 e 5 ) n\ \ (1\leq n\leq 2\mathrm{e}5) n  (1n2e5)的序列 a 1 , ⋯   , a n a_1,\cdots,a_n a1,,an b 1 , ⋯   , b n    ( 1 ≤ a i , b i ≤ 1 e 9 ) b_1,\cdots,b_n\ \ (1\leq a_i,b_i\leq 1\mathrm{e}9) b1,,bn  (1ai,bi1e9).将序列 a [ ] a[] a[]重排为序列 a ′ [ ]   s . t .   ∑ i = 1 n f ( a i , b i ) a'[]\ s.t.\ \displaystyle\sum_{i=1}^n f(a_i,b_i) a[] s.t. i=1nf(ai,bi)最大,输出任一满足条件的序列 a ′ [ ] a'[] a[].

思路

n n n个元素中选 k k k个元素的方案数为 C n k C_n^k Cnk.枚举 i i i,为使得其是子集中最小的元素,应在比它大的 ( n − i ) (n-i) (ni)个元素中选 ( k − 1 ) (k-1) (k1)个元素,故 f ( n , k ) = ∑ i = 1 n − k + 1 i ⋅ C n − i k − 1 C n k f(n,k)=\dfrac{\displaystyle\sum_{i=1}^{n-k+1} i\cdot C_{n-i}^{k-1}}{C_n^k} f(n,k)=Cnki=1nk+1iCnik1.

注意到 f ( n , k ) f ( n , k − 1 ) = k k + 1 \dfrac{f(n,k)}{f(n,k-1)}=\dfrac{k}{k+1} f(n,k1)f(n,k)=k+1k,则 f ( n , k ) = n + 1 k + 1 f(n,k)=\dfrac{n+1}{k+1} f(n,k)=k+1n+1.为使得 f ( n , k ) f(n,k) f(n,k)尽量大,应让较大的 n n n与较小的 k k k配对.

将序列 a [ ] a[] a[]降序排列、 b [ ] b[] b[]升序排列后配对即可.

代码

void solve() {
  int n; cin >> n;
  vi a(n);
  vii b(n);
  for (auto& ai : a) cin >> ai;
  for (int i = 0; i < n; i++) {
    cin >> b[i].first;
    b[i].second = i;
  }

  sort(all(a), greater<int>()), sort(all(b));

  vi ans(n);
  for (int i = 0; i < n; i++) ans[b[i].second] = a[i];
  for (int i = 0; i < n; i++) cout << ans[i] << " \n"[i == n - 1];
}

int main() {
  solve();
}


844B. Rectangles

原题指路:https://codeforces.com/problemset/problem/844/B

题意

给定一个 n × m    ( 1 ≤ n , m ≤ 50 ) n\times m\ \ (1\leq n,m\leq 50) n×m  (1n,m50) 0 − 1 0-1 01矩阵.将元素分为若干个非空集合,使得集合内的数字相等,且任意两个集合中的元素同行或同列,求集合数.

思路

注意到符合要求的集合只能包含一行或一列,统计每行、每列的 0 0 0 1 1 1的个数,不妨设第 i i i行的 0 0 0的个数为 r o w 0 row_0 row0,则该行对答案的贡献为 2 r o w 0 − 1 2^{row_0}-1 2row01,即幂集减去空集.对每行和每列的答案求和,因单个元素在行和列都被计算了一次,答案减去 n m nm nm即可.

代码

void solve() {
  int n, m; cin >> n >> m;
  vi row0(n), row1(n);  // 每行0、1的个数
  vi col0(m), col1(m);  // 每列0、1的个数
  for (int i = 0; i < n; i++) {
    for (int j = 0; j < m; j++) {
      int a; cin >> a;
      if (a == 0) row0[i]++, col0[j]++;
      else row1[i]++, col1[j]++;
    }
  }

  ll ans = -n * m;
  for (int i = 0; i < n; i++) ans += ((ll)1 << row0[i]) - 1 + ((ll)1 << row1[i]) - 1;
  for (int i = 0; i < m; i++) ans += ((ll)1 << col0[i]) - 1 + ((ll)1 << col1[i]) - 1;
  cout << ans;
}

int main() {
  solve();
}


888D. Almost Identity Permutations

原题指路:https://codeforces.com/problemset/problem/888/D

题意 ( 2   s 2\ \mathrm{s} 2 s)

给定两整数 n , k n,k n,k.称一个 1 ∼ n 1\sim n 1n排列 p p p是好的,如果存在至少 ( n − k ) (n-k) (nk)个下标 i    ( 1 ≤ i ≤ n )   s . t .   p i = i i\ \ (1\leq i\leq n)\ s.t.\ p_i=i i  (1in) s.t. pi=i.

给定两整数 n , k    ( 1 ≤ k ≤ 4 ≤ n ≤ 1000 ) n,k\ \ (1\leq k\leq 4\leq n\leq 1000) n,k  (1k4n1000),求好的排列的个数.

思路

考虑如何求恰有 m    ( 1 ≤ m ≤ k ) m\ \ (1\leq m\leq k) m  (1mk)个下标 i    ( 1 ≤ i ≤ n )   s . t .   p i ≠ i i\ \ (1\leq i\leq n)\ s.t.\ p_i\neq i i  (1in) s.t. pi=i的排列的个数.先从 n n n个数中选出 m m m个数作为满足 p i ≠ i p_i\neq i pi=i的数的下标,有 C n m C_n^m Cnm种情况.将这 m m m个数错排,共 f ( m ) f(m) f(m)种情况,其中 f ( n ) f(n) f(n)表示 1 ∼ n 1\sim n 1n的排列的错排数,可递推求得.故 a n s = ∑ m = 1 k C n m f ( m ) \displaystyle ans=\sum_{m=1}^k C_n^m f(m) ans=m=1kCnmf(m).当 m = 1 m=1 m=1时,错排数 f ( m ) = 0 f(m)=0 f(m)=0,但事实上 k = 1 k=1 k=1时存在一种满足的方案 1 , ⋯   , n 1,\cdots,n 1,,n,故 a n s = 1 + ∑ m = 2 k C n m f ( m ) \displaystyle ans=1+\sum_{m=2}^k C_n^m f(m) ans=1+m=2kCnmf(m).

代码

const int MAXN = 1005;
ll C[MAXN][MAXN];  // C[i][j]表示组合数C(i,j)
int f[MAXN];  // 错排数

void init() {  // 预处理C[][]和f[]
  for (int i = 0; i < MAXN; i++) {
    for (int j = 0; j <= i; j++) {
      if (!j) C[i][j] = 1;  // 初始条件
      else C[i][j] = C[i - 1][j] + C[i - 1][j - 1];
    }
  }

  f[1] = 0, f[2] = 1;
  for (int i = 3; i < 5; i++) f[i] = (i - 1) * (f[i - 1] + f[i - 2]);
}

void solve() {
  init();

  int n, k; cin >> n >> k;

  ll ans = 1;  // 特判i=1;
  for (int i = 2; i <= k; i++) ans += C[n][i] * f[i];
  cout << ans;
}

int main() {
  solve();
}


978E. Bus Video System

原题指路:https://codeforces.com/problemset/problem/978/E

题意

某公交系统会记录每一站车上人数的变化.若上一站车上有 x x x人,该站车上有 y y y人,则记录 ( y − x ) (y-x) (yx).有 n n n个公交站,在每个公交站记录的数据分别是 a 1 , ⋯   , a n a_1,\cdots,a_n a1,,an.设车的最大容量为 w w w人(无需考虑司机),问初始时车上的人数有几种可能,若无合法方案,输出 0 0 0.

第一行输入两个整数 n , w    ( 1 ≤ n ≤ 1000 , 1 ≤ w ≤ 1 e 9 ) n,w\ \ (1\leq n\leq 1000,1\leq w\leq 1\mathrm{e}9) n,w  (1n1000,1w1e9).第二行输入 n n n个整数 a 1 , ⋯   , a n    ( − 1 e 6 ≤ a i ≤ 1 e 6 ) a_1,\cdots,a_n\ \ (-1\mathrm{e}6\leq a_i\leq 1\mathrm{e}6) a1,,an  (1e6ai1e6).

思路

设初始时车上的人数为 x x x.设 a [ ] a[] a[]的前缀和为 p r e [ ] , m a x d = max ⁡ 1 ≤ i ≤ n p r e i , m i n d = min ⁡ 1 ≤ i ≤ n p r e i pre[],\displaystyle maxd=\max_{1\leq i\leq n}pre_i,mind=\min_{1\leq i\leq n}pre_i pre[],maxd=1inmaxprei,mind=1inminprei,则全程车上的最大人数为 x + m a x d x+maxd x+maxd,最小人数为 x + m i n d x+mind x+mind.

由车的最大容量和人数非负知: x + m a x d ≤ w , x + m i n d ≥ 0 x+maxd\leq w,x+mind\geq 0 x+maxdw,x+mind0,该不等式组的整数解个数 a n s = max ⁡ { w − m a x d + m i n d + 1 } ans=\max\{w-maxd+mind+1\} ans=max{wmaxd+mind+1}.

注意 m a x d maxd maxd m i n d mind mind不能初始化为 − I N F -INF INF I N F INF INF,因为车上的人数最小为 0 0 0,都初始化为 0 0 0即可.

代码

void solve() {
  int n, w; cin >> n >> w;
  int pre = 0;  // a[]的前缀和
  int maxd = 0, mind = 0;  // 当前pre[]的最大值、最小值,注意初始化
  while (n--) {
    int a; cin >> a;
    pre += a;
    maxd = max(maxd, pre), mind = min(mind, pre);
  }
  cout << max(0, w - maxd + mind + 1);
}

int main() {
  solve();
}


1007A. Reorder the Array

原题指路:https://codeforces.com/problemset/problem/1007/A

题意 ( 2   s 2\ \mathrm{s} 2 s)

给定一个长度为 n    ( 1 ≤ n ≤ 1 e 5 ) n\ \ (1\leq n\leq 1\mathrm{e}5) n  (1n1e5)的整数序列 a 1 , ⋯   , a n    ( 1 ≤ a i ≤ 1 e 9 ) a_1,\cdots,a_n\ \ (1\leq a_i\leq 1\mathrm{e}9) a1,,an  (1ai1e9),将其重新排列为序列 b [ ] b[] b[].求一个序列 b [ ]   s . t .   ∑ i = 1 n [ b i > a i ] b[]\ s.t.\ \displaystyle \sum_{i=1}^n [b_i>a_i] b[] s.t. i=1n[bi>ai]最大,输出最大值.

思路

a [ ] a[] a[]升序排列,对每个 a i a_i ai,考察其后有多少个 a j    ( i > j )   s . t .   a j > a i a_j\ \ (i>j)\ s.t.\ a_j>a_i aj  (i>j) s.t. aj>ai,答案即为所有 a i a_i ai的结果之和,用双指针即可做到 O ( n ) O(n) O(n).

代码

void solve() {
  int n; cin >> n;
  vi a(n);
  for (auto& ai : a) cin >> ai;

  sort(all(a));
  int ans = 0;
  for (int i = 0, j = 1; i < n - 1 && j < n; i++, j++) {
    if (a[j] > a[i]) ans++;
    else i--;
  }
  cout << ans;
}

int main() {
  solve();
}


1081C. Colorful Bricks

原题指路:https://codeforces.com/problemset/problem/1081/C

题意 ( 2   s 2\ \mathrm{s} 2 s)

m m m种颜色给排成一行的 n n n块砖染色,要求恰有 k k k块砖与其左边的砖的颜色不同,求染色方案数,答案对 998244353 998244353 998244353取模.

第一行输入三个整数 n , m , k    ( 1 ≤ n , m ≤ 2000 , 0 ≤ k ≤ n − 1 ) n,m,k\ \ (1\leq n,m\leq 2000,0\leq k\leq n-1) n,m,k  (1n,m2000,0kn1).

思路I

d p [ i ] [ j ] dp[i][j] dp[i][j]表示前 i i i块砖中有 j j j块砖与其左边的砖的颜色不同的方案数,则 a n s = d p [ n ] [ k ] ans=dp[n][k] ans=dp[n][k].

根据第 i i i块砖与第 ( i − 1 ) (i-1) (i1)块砖颜色是否相同分类,状态转移方程 d p [ i ] [ j ] = d p [ i − 1 ] [ j ] + ( m − 1 ) ⋅ d p [ i − 1 ] [ j − 1 ] dp[i][j]=dp[i-1][j]+(m-1)\cdot dp[i-1][j-1] dp[i][j]=dp[i1][j]+(m1)dp[i1][j1].初始条件 d p [ i ] [ 0 ] = m dp[i][0]=m dp[i][0]=m,即 1 ∼ i 1\sim i 1i号砖都染同种颜色的方案数.

代码I

const int MAXN = 2005;
const int MOD = 998244353;
int n, m, k;  // 砖数、颜色数、不同数
int dp[MAXN][MAXN];  // dp[i][j]表示前i块砖中有j块砖与其左边的砖的颜色不同的方案数

void solve() {
  cin >> n >> m >> k;

  for (int i = 1; i <= n; i++) {
    dp[i][0] = m;
    for (int j = 1; j <= k; j++)
      dp[i][j] = ((ll)dp[i - 1][j] + (ll)(m - 1) * dp[i - 1][j - 1]) % MOD;
  }
  cout << dp[n][k];
}

int main() {
  solve();
}

思路II

将一段连续的相同颜色的砖视为一个部分,将序列分为 ( k + 1 ) (k+1) (k+1)个部分,使得相邻颜色不同只出现在相邻两部分的交界处.第一个部分颜色有 m m m种选择,后面每个部分与前面的部分颜色不同,故有 m ( m − 1 ) k m(m-1)^k m(m1)k种情况.

在长度为 n n n的序列的 ( n − 1 ) (n-1) (n1)个空中选 k k k个作为相邻两部分的分界点,有 C n − 1 k C_{n-1}^k Cn1k种情况,故 a n s = C n − 1 k m ( m − 1 ) k ans=C_{n-1}^k m(m-1)^k ans=Cn1km(m1)k.

代码II

const int MAXN = 2005;
const int MOD = 998244353;
int n, m, k;  // 砖数、颜色数、不同数
int C[MAXN][MAXN];  // C[i][j]表示组合数C(i,j)

void init() {
  for (int i = 0; i < MAXN; i++) {
    for (int j = 0; j <= i; j++) {
      if (!j) C[i][j] = 1;  // 初始条件
      else C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % MOD;
    }
  }
}

void solve() {
  init();

  cin >> n >> m >> k;
  cout << (ll)C[n - 1][k] * m % MOD * qpow(m - 1, k, MOD) % MOD;
}

int main() {
  solve();
}


1084C. The Fair Nut and String

原题指路:https://codeforces.com/problemset/problem/1084/C

题意

给定一个长度不超过 1 e 5 1\mathrm{e}5 1e5的只包含小写英文字母的字符串 s s s,求满足如下条件的严格递增的整数序列 p 1 , ⋯   , p k p_1,\cdots,p_k p1,,pk的个数,答案对 1 e 9 + 7 1\mathrm{e}9+7 1e9+7取模:① s p i = a    ( 1 ≤ i ≤ k ) s_{p_i}=a\ \ (1\leq i\leq k) spi=a  (1ik);②对 ∀ i ∈ [ 1 , k ) , ∃ j   s . t .   p i < j < p i + 1 \forall i\in [1,k),\exists j\ s.t.\ p_i<j<p_{i+1} i[1,k),j s.t. pi<j<pi+1 s j = b s_j=b sj=b.

思路

显然 s s s中非’a’、'b’的字符不会对答案产生贡献.

显然满足条件的下标 p i p_i pi j j j对应的子列有形式$abab\cdots $.对每个’a’,找到其左边第一个’b’,则以该’b’结尾的所有序列后接上当前的’a’仍是合法序列.

用一个变量 l a s t last last记录上一次遇到’b’时求得的答案,初始时 l a s t = 0 last=0 last=0.①遇到’a’时, a n s + = l a s t + 1 ans+=last+1 ans+=last+1;②遇到’b’时, l a s t = a n s last=ans last=ans.

代码

const int MOD = 1e9 + 7;

void solve() {
  string s; cin >> s;

  int ans = 0;
  int last = 0;  // 上一次遇到'b'时求得的答案
  for (auto ch : s) {
    if (ch == 'a') ans = (ans + last + 1) % MOD;
    else if (ch == 'b') last = ans;
  }
  cout << ans;
}

int main() {
  solve();
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值