[Codeforces] combinatorics (R1600) Part.7

[Codeforces] combinatorics (R1600) Part.7

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

1534C. Little Alawn’s Puzzle

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

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

称一个 2 × n 2\times n 2×n的整型矩阵是好的,如果其两行分别都是 1 ∼ n 1\sim n 1n的排列,且同一列的两个数不同.给定一个好的矩阵.现有操作:交换同一列的两个数.问若干次操作后使得矩阵仍是好的的方案数,答案对 1 e 9 + 7 1\mathrm{e}9+7 1e9+7取模.

t    ( 1 ≤ t ≤ 1 e 4 ) t\ \ (1\leq t\leq 1\mathrm{e}4) t  (1t1e4)组测试数据.每组测试数据第一行输入一个整数 n    ( 2 ≤ n ≤ 4 e 5 ) n\ \ (2\leq n\leq 4\mathrm{e}5) n  (2n4e5).接下来输入一个 2 × n 2\times n 2×n的好的矩阵.数据保证所有测试数据的 n n n之和不超过 4 e 5 4\mathrm{e}5 4e5.

思路

注意到交换一列的两个数后,为使得同一行的数不同,还需交换若干列的两个数,显然这样的关系构成一个循环置换.用并查集统计置换环的个数 c n t cnt cnt,则 a n s = 2 c n t ans=2^{cnt} ans=2cnt.

代码

const int MAXN = 4e5 + 5;
const int MOD = 1e9 + 7;
namespace DSU {  // 求置换环
  int n;  // 元素个数
  int fa[MAXN];  // 下标从1开始

  void init() {
    for (int i = 1; i <= n; i++) fa[i] = i;
  }

  int find(int x) {
    return x == fa[x] ? x : fa[x] = find(fa[x]);
  }

  void merge(int x, int y) {
    if ((x = find(x)) != (y = find(y))) fa[x] = y;
  }

  int countComponent() {
    int res = 0;
    for (int i = 1; i <= n; i++) res += fa[i] == i;
    return res;
  }
}
using namespace DSU;

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

  init();
  for (int i = 0; i < n; i++) {
    int b; cin >> b;
    merge(a[i], b);
  }

  cout << qpow(2, countComponent(), MOD) << endl;
}

int main() {
  CaseT  // 单测时注释掉该行
    solve();
}


1552B. Running for Gold

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

题意

编号 1 ∼ n 1\sim n 1n n n n个运动员参加了编号 1 ∼ 5 1\sim 5 15 5 5 5场比赛,其中 i   ( 1 ≤ i ≤ n ) i\ (1\leq i\leq n) i (1in)号运动员在第 j    ( 1 ≤ j ≤ 5 ) j\ \ (1\leq j\leq 5) j  (1j5)比赛的排名为 r i j r_{ij} rij.称 x x x号运动员优于 y y y号运动员,如果至少有 3 3 3场比赛中 x x x号运动员的排名在 y y y号运动员之前.若一个运动员优于其他所有运动员,则他可以夺金.求最终夺金的运动员的编号,无解输出 − 1 -1 1.

t    ( 1 ≤ t ≤ 1000 ) t\ \ (1\leq t\leq 1000) t  (1t1000)组测试数据.每组测试数据第一行输入一个整数 n    ( 1 ≤ n ≤ 5 e 4 ) n\ \ (1\leq n\leq 5\mathrm{e}4) n  (1n5e4).接下来输入一个 n × 5 n\times 5 n×5的整型矩阵,其中第 i    ( 1 ≤ i ≤ n ) i\ \ (1\leq i\leq n) i  (1in)行的元素分别为 r i 1 , r i 2 , r i 3 , r i 4 , r i 5    ( 1 ≤ r i j ≤ n ) r_{i1},r_{i2},r_{i3},r_{i4},r_{i5}\ \ (1\leq r_{ij}\leq n) ri1,ri2,ri3,ri4,ri5  (1rijn).数据保证所有测试数据的 n n n之和不超过 5 e 4 5\mathrm{e}4 5e4.

思路I

至多只会有一个运动员夺金.

[] 若不然,则至少有两个运动员 x x x y y y夺金,此时 x x x不优于 y y y,与 x x x夺金的条件矛盾.

1 1 1号运动员与其他运动员两两比较,找到一个可能夺金的运动员,即排名在比较过程中始终领先的运动员,再检查该运动员是否优于其他运动员即可.

代码I

struct Rank {
  int rank[5];

  bool operator<(const Rank& B) {
    int cnt = 0;
    for (int i = 0; i < 5; i++) cnt += rank[i] < B.rank[i];
    return cnt >= 3;
  }
};

void solve() {
  int n; cin >> n;
  vector<Rank> a(n);
  for (int i = 0; i < n; i++)
    for (int j = 0; j < 5; j++) cin >> a[i].rank[j];

  int gold = 0;  // 夺金候选人
  for (int i = 1; i < n; i++)
    if (a[i] < a[gold]) gold = i;

  // 检查gold号运动员是否夺金
  for (int i = 0; i < n; i++) {
    if (gold != i && a[i] < a[gold]) {
      cout << -1 << endl;
      return;
    }
  }
  cout << gold + 1 << endl;
}

int main() {
  CaseT  // 单测时注释掉该行
    solve();
}

思路II

对每个运动员 i i i,随机遍历其他运动员,直至找到一个优于 i i i的运动员.下证这样的时间复杂度为 O ( n log ⁡ n ) O(n\log n) O(nlogn).

[引理] 对 ∀ k ≥ 0 \forall k\geq 0 k0,至多有 ( 2 k + 1 ) (2k+1) (2k+1)个运动员优于至少 ( n − k − 1 ) (n-k-1) (nk1)个其他运动员.

[引.证] 设有 m m m个运动员优于至少 ( n − k − 1 ) (n-k-1) (nk1)个其他运动员.

​ 考察一个包含编号 1 ∼ m 1\sim m 1m m m m个节点的有向边图,其中存在边 i → j i\rightarrow j ij当且仅当运动员 i i i优于运动员 j j j.

​ 每个节点的出度 ≥ m − k − 1 \geq m-k-1 mk1,则总边数 ≥ m ( m − k − 1 ) \geq m(m-k-1) m(mk1).

​ 注意到包含 m m m个节点的有向图至多 C m 2 C_m^2 Cm2条边,则 m ( m − k − 1 ) ≤ m ( m − 1 ) 2 m(m-k-1)\leq \dfrac{m(m-1)}{2} m(mk1)2m(m1),即 m ≤ 2 k + 1 m\leq 2k+1 m2k+1.

[] 考察一个恰优于其他 ( n − k − 1 ) (n-k-1) (nk1)个运动员的运动员 i i i,计算确定 i i i是否夺金所需的期望比较次数.

k = 0 k=0 k=0时, i i i号运动员需与其他 ( n − 1 ) (n-1) (n1)个运动员比较,期望比较次数为 O ( n ) O(n) O(n).

k ≥ 1 k\geq 1 k1时,从 1 ∼ n 1\sim n 1n中随机选 k k k个数,抽到排名最靠前的运动员的期望步数为 O ( n k ) O\left(\dfrac{n}{k}\right) O(kn).

时间复杂度 O ( q 0 ⋅ n + ∑ k = 1 n − 1 q k ⋅ n k ) = O ( n ( q 0 + ∑ k = 1 n − 1 q k k ) ) \displaystyle O\left(q_0\cdot n+\sum_{k=1}^{n-1} q_k\cdot \dfrac{n}{k}\right)=O\left(n\left(q_0+\sum_{k=1}^{n-1} \dfrac{q_k}{k}\right)\right) O(q0n+k=1n1qkkn)=O(n(q0+k=1n1kqk)),

​ 其中 q k q_k qk为恰优于其他 ( n − k − 1 ) (n-k-1) (nk1)个运动员的运动员的个数.

s k = q 0 + ⋯ + a k s_k=q_0+\cdots+a_k sk=q0++ak,则时间复杂度 O ( n ( s 0 + ∑ k = 1 n − 1 s k − s k − 1 k ) ) = O ( n ( ∑ k = 1 n − 2 s k k ( k + 1 ) + s n − 1 n − 1 ) ) \displaystyle O\left(n\left(s_0+\sum_{k=1}^{n-1} \dfrac{s_k-s_{k-1}}{k}\right)\right)=O\left(n\left(\sum_{k=1}^{n-2} \dfrac{s_k}{k(k+1)}+\dfrac{s_{n-1}}{n-1}\right)\right) O(n(s0+k=1n1ksksk1))=O(n(k=1n2k(k+1)sk+n1sn1)).

由引理知: s k ≤ 2 k + 1 s_k\leq 2k+1 sk2k+1,故时间复杂度 ≤ O ( n ( ∑ k = 1 n − 2 2 k + 1 k ( k + 1 ) + 2 n − 1 n − 1 ) ) = O ( 2 n ∑ k = 1 n − 2 1 k ) = O ( n log ⁡ n ) \displaystyle \leq O\left(n\left(\sum_{k=1}^{n-2} \dfrac{2k+1}{k(k+1)}+\dfrac{2n-1}{n-1}\right)\right)=O\left(2n\sum_{k=1}^{n-2}\dfrac{1}{k}\right)=O(n\log n) O(n(k=1n2k(k+1)2k+1+n12n1))=O(2nk=1n2k1)=O(nlogn).

代码II

struct Rank {
  int rank[5];

  bool operator<(const Rank& B) {
    int cnt = 0;
    for (int i = 0; i < 5; i++) cnt += rank[i] < B.rank[i];
    return cnt >= 3;
  }
};

void solve() {
  srand(time(0));

  int n; cin >> n;
  vector<Rank> a(n);
  for (int i = 0; i < n; i++)
    for (int j = 0; j < 5; j++) cin >> a[i].rank[j];

  vi p(n);  // 置换
  for (int i = 0; i < n; i++) p[i] = i;
  random_shuffle(all(p));

  for (int i = 0; i < n; i++) {
    bool gold = true;  // 记录i号运动员能否夺金
    for (int j = 0; j < n && gold; j++)
      if (i != j) gold &= a[p[i]] < a[p[j]];

    if (gold) {
      cout << p[i] + 1 << endl;
      return;
    }
  }
  cout << -1 << endl;
}

int main() {
  CaseT  // 单测时注释掉该行
    solve();
}


1567C. Carrying Conundrum

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

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

在这里插入图片描述

考虑如上图所示的加法进位模式,即进位到当前列左边的第二列.给定一个正整数 n n n,求用上述方式会求得 a + b = n a+b=n a+b=n的有序正整数对 ( a , b ) (a,b) (a,b)的个数,其中 ( a , b ) (a,b) (a,b) ( b , a ) (b,a) (b,a)不同当且仅当 a ≠ b a\neq b a=b.

t    ( 1 ≤ t ≤ 1000 ) t\ \ (1\leq t\leq 1000) t  (1t1000)组测试数据.每组测试数据输入一个整数 n    ( 2 ≤ n ≤ 1 e 9 ) n\ \ (2\leq n\leq 1\mathrm{e}9) n  (2n1e9).

思路I

注意到上述加法进位模式事实上独立了奇数位和偶数位的运算,设 n n n的奇数位表示的数为 x x x,偶数位表示的数为 y y y.注意到将一个正整数 m m m拆分为两个非负整数之和有 ( m + 1 ) (m+1) (m+1)种方案,则 a n s = ( x + 1 ) ( y + 1 ) − 2 ans=(x+1)(y+1)-2 ans=(x+1)(y+1)2,其中 − 2 -2 2是因为两个数对 ( a , b ) (a,b) (a,b)的第一项同为 0 0 0或第二项同为 0 0 0时不合法.

代码I

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

  int n = s.length();
  int x = 0, y = 0;
  for (int i = 0; i < n; i += 2) x = x * 10 + s[i] - '0';
  for (int i = 1; i < n; i += 2) y = y * 10 + s[i] - '0';
  cout << (x + 1) * (y + 1) - 2 << endl;
}

int main() {
  CaseT  // 单测时注释掉该行
    solve();
}


1569C. Jury Meeting

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

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

编号 1 ∼ n 1\sim n 1n n n n个人开会,其中 i    ( 1 ≤ i ≤ n ) i\ \ (1\leq i\leq n) i  (1in)号人有 a i a_i ai个问题需要提出.现用一个 1 ∼ n 1\sim n 1n的排列 p p p来确定他们的发言顺序,轮到对应的人时,若他还有问题需要提出,则提出一个问题;否则跳过.重复该过程直至所有人的问题都提出完.称一个排列 p p p是好的,如果以 p p p为发言顺序时不存在一个人连续提出问题.求好的排列的个数,答案对 998244353 998244353 998244353取模.

t    ( 1 ≤ t ≤ 1 e 4 ) t\ \ (1\leq t\leq 1\mathrm{e}4) t  (1t1e4)组测试数据.每组测试数据第一行输入一个整数 n    ( 2 ≤ n ≤ 2 e 5 ) n\ \ (2\leq n\leq 2\mathrm{e}5) n  (2n2e5).第二行输入 n n n个整数 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).数据保证所有测试数据的 n n n之和不超过 2 e 5 2\mathrm{e}5 2e5.

思路

①若序列中的最大值的出现次数 ≥ 2 \geq 2 2,显然任一排列都是好的, a n s = n ! ans=n! ans=n!.

②若序列中的最大值只出现一次,设它在下标 x x x处出现.

​ 在第 a x a_x ax轮时只有一个人提出问题,在第 ( a x − 1 ) (a_x-1) (ax1)轮时至少有一个人在第 x x x个人之后提出问题.

​ 设序列中值为 ( a x − 1 ) (a_x-1) (ax1)的元素有 k k k个,显然序列中值不为 a x a_x ax ( a x − 1 ) (a_x-1) (ax1)的元素可任意排列,有 A n n − k − 1 = n ! ( k + 1 ) ! A_n^{n-k-1}=\dfrac{n!}{(k+1)!} Annk1=(k+1)!n!种情况.

​ 若 k k k个值为 ( a x − 1 ) (a_x-1) (ax1)的人中至少有一个在第 x x x个人之后提出问题,则排列是好的.

​ 下面计算不好的排列的个数.显然排列是不好的当且仅当值为 a x a_x ax的人排在最后,有 k ! k! k!种情况.

​ 故不好的排列有 n ! ( k + 1 ) ! ⋅ k ! = n ! k + 1 \dfrac{n!}{(k+1)!}\cdot k!=\dfrac{n!}{k+1} (k+1)!n!k!=k+1n!个,则 a n s = n ! − n ! k + 1 ans=n!-\dfrac{n!}{k+1} ans=n!k+1n!.

代码

const int MAXN = 2e5 + 5;
const int MOD = 998244353;
int fac[MAXN];

void init() {
  fac[0] = 1;
  for (int i = 1; i < MAXN; i++) fac[i] = (ll)fac[i - 1] * i % MOD;
}

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

  int maxnum = *max_element(all(a));
  if (count(all(a), maxnum) >= 2) {
    cout << fac[n] << endl;
    return;
  }

  int k = count(all(a), maxnum - 1);
  cout << (((ll)fac[n] - (ll)fac[n] * qpow(k + 1, MOD - 2, MOD)) % MOD + MOD) % MOD << endl;
}

int main() {
  init();
  CaseT  // 单测时注释掉该行
    solve();
}


1594E1. Rubik’s Cube Coloring (easy version)

原题指路:https://codeforces.com/problemset/problem/1594/E1

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

在这里插入图片描述

用如上图所示的魔方的六种颜色给一棵包含 ( 2 k − 1 ) (2^k-1) (2k1)个节点的满二叉树的节点染色,要求任意两相邻的节点染的颜色在魔方上也相邻,求染色方案数,答案对 1 e 9 + 7 1\mathrm{e}9+7 1e9+7取模.

第一行输入一个整数 k    ( 1 ≤ k ≤ 60 ) k\ \ (1\leq k\leq 60) k  (1k60).

思路I

根节点有 6 6 6种选择,根节点颜色确定后从上往下每个节点有 4 4 4种选择,故 a n s = 6 ⋅ 4 2 k − 2 ans=6\cdot 4^{2^k-2} ans=642k2.

代码I

const int MOD = 1e9 + 7;

void solve() {
  int k; cin >> k;
  cout << (ll)6 * qpow(4, ((ll)1 << k) - 2, MOD) % MOD;
}

int main() {
  solve();
}

思路II

通过颜色是否不同、颜色之和是否不为 7 7 7判断颜色能否相邻:①白色为 1 1 1,黄色为 6 6 6;②绿色为 2 2 2,蓝色为 5 5 5;③红色为 3 3 3,橙色为 4 4 4.

d p [ i ] [ j ] dp[i][j] dp[i][j]表示深度为 i i i的满二叉树根节点染 j ∈ [ 1 , 6 ] j\in [1,6] j[1,6]色的方案数,则 a n s = ∑ j = 1 6 d p [ k ] [ j ] \displaystyle ans=\sum_{j=1}^6 dp[k][j] ans=j=16dp[k][j].

状态转移即枚举深度、根节点颜色、左儿子节点颜色、右儿子节点颜色,初始条件 d p [ 1 ] [ j ] = 1    ( j = 1 , ⋯   , 6 ) dp[1][j]=1\ \ (j=1,\cdots,6) dp[1][j]=1  (j=1,,6).

代码II

const int MAXN = 65;
const int MOD = 1e9 + 7;
int dp[MAXN][7];  // dp[i][j]表示深度为i的满二叉树根节点染j色的方案数

void solve() {
  int n; cin >> n;

  for (int i = 1; i <= 6; i++) dp[1][i] = 1;  // 初始条件

  for (int i = 2; i <= n; i++) {  // 枚举深度
    for (int j = 1; j <= 6; j++) {  // 枚举根节点颜色
      for (int k = 1; k <= 6; k++) {  // 枚举左儿子节点的颜色
        if (j != k && j + k != 7) {
          for (int l = 1; l <= 6; l++) {  // 枚举右儿子节点的颜色
            if (j != l && j + l != 7)
              dp[i][j] = ((ll)dp[i][j] + (ll)dp[i - 1][k] * dp[i - 1][l] % MOD) % MOD;
          }
        }
      }
    }
  }

  int ans = 0;
  for (int i = 1; i <= 6; i++) ans = (ans + dp[n][i]) % MOD;
  cout << ans;
}

int main() {
  solve();
}


1614C. Divan and bitwise operations

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

题意

对一个长度为 n n n的序列 a 1 , ⋯   , a n a_1,\cdots,a_n a1,,an,定义其权值为其所有非空子列的元素异或和之和.,给定其中 m m m个区间的元素或值,每个元素至少包含在一个区间中.求 a [ ] a[] a[]的权值,答案对 1 e 9 + 7 1\mathrm{e}9+7 1e9+7取模.若有多组解,输出任一组解.

t    ( 1 ≤ t ≤ 1000 ) t\ \ (1\leq t\leq 1000) t  (1t1000)组测试数据.每组测试数据第一行输入两个整数 n , m    ( 1 ≤ n , m ≤ 2 e 5 ) n,m\ \ (1\leq n,m\leq 2\mathrm{e}5) n,m  (1n,m2e5).接下来 m m m行每行输入三个整数 l , r , x    ( 1 ≤ l ≤ r ≤ n , 0 ≤ x ≤ 2 30 − 1 ) l,r,x\ \ (1\leq l\leq r\leq n,0\leq x\leq 2^{30}-1) l,r,x  (1lrn,0x2301).数据保证序列 a 1 , ⋯   , a n a_1,\cdots,a_n a1,,an的每个元素至少包含在一个区间中,且至少存在一个序列满足要求,所有测试数据的 n n n之和、 m m m之和都不超过 2 e 5 2\mathrm{e}5 2e5.

思路

考察所有元素的二进制表示的每一位对答案的贡献.

对二进制第 i i i位,若没有元素在该位为 1 1 1,则该位对答案无贡献;否则该位包含奇数个 1 1 1的子区间会对答案产生贡献.

x x x为序列所有元素的或,亦即所有子区间的或.

下面证明二进制第 i i i位包含奇数个 1 1 1的子列有 2 n − 1 2^{n-1} 2n1个,则 a n s = x ⋅ 2 n − 1 ans=x\cdot 2^{n-1} ans=x2n1.

[] 将所有元素分为两个集合 A A A B B B,其中 A A A中的元素的二进制表示的第 i i i位都为 0 0 0, B B B中的元素的二进制表示的第 i i i位都为 1 1 1.

①取 A A A的任一子集都不影响二进制第 i i i 1 1 1的出现次数的奇偶性,故不影响答案,有 2 ∣ A ∣ 2^{|A|} 2A种取法.

②令 k = [ ∣ B ∣ 是偶数 ] k=[|B|是偶数] k=[B是偶数].为使得二进制第 i i i 1 1 1的出现奇数次,应从 B B B中取奇数个元素,

​ 有 C ∣ B ∣ 1 + C ∣ B ∣ 3 + ⋯ + C ∣ B ∣ ∣ B ∣ − k = 2 ∣ B ∣ − 1 C_{|B|}^1+C_{|B|}^3+\cdots+C_{|B|}^{|B|-k}=2^{|B|-1} CB1+CB3++CBBk=2B1种取法.

综上,二进制第 i i i位包含奇数个 1 1 1的子列有 2 ∣ A ∣ ⋅ 2 ∣ B ∣ − 1 = 2 n − 1 2^{|A|}\cdot 2^{|B|-1}=2^{n-1} 2A2B1=2n1个.

代码

const int MOD = 1e9 + 7;

void solve() {
  int n; cin >> n;
  int tmp = 0;  // 序列所有元素的或
  CaseT{
    int l, r, x; cin >> l >> r >> x;
    tmp |= x;
  }

  cout << (ll)tmp * qpow(2, n - 1, MOD) % MOD << endl;
}

int main() {
  CaseT  // 单测时注释掉该行
    solve();
}


1648A. Weird Sum

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

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

给定一个 n × m n\times m n×m的表格,每个格子有一种颜色,每种颜色用一个 1 ∼ 1 e 5 1\sim 1\mathrm{e}5 11e5的整数表示.对所有颜色,求任意两相同颜色的格子间的Manhattan距离之和.

第一行输入两个整数 n , m    ( 1 ≤ n ≤ m , 1 ≤ n m ≤ 1 e 5 ) n,m\ \ (1\leq n\leq m,1\leq nm\leq 1\mathrm{e}5) n,m  (1nm,1nm1e5).接下来输入一个 n × m n\times m n×m的元素范围为 [ 1 , 1 e 5 ] [1,1\mathrm{e}5] [1,1e5]的整型矩阵,表示表格的染色情况.

思路I

设某种颜色有 ( r 0 , c 0 ) , ⋯   , ( r k − 1 , c k − 1 ) (r_0,c_0),\cdots,(r_{k-1},c_{k-1}) (r0,c0),,(rk1,ck1)的格子,则该颜色对答案的贡献为 ∑ i = 0 k − 1 ∑ j = i + 1 k − 1 ( ∣ r i − r j ∣ + ∣ c i − c j ∣ ) = ( ∑ i = 0 k − 1 ∑ j = i + 1 k − 1 ∣ r i − r j ∣ ) + ( ∑ i = 0 k − 1 ∑ j = i + 1 k − 1 ∣ c i − c j ∣ ) \displaystyle \sum_{i=0}^{k-1}\sum_{j=i+1}^{k-1}(|r_i-r_j|+|c_i-c_j|)=\left(\sum_{i=0}^{k-1} \sum_{j=i+1}^{k-1} |r_i-r_j|\right)+\left(\sum_{i=0}^{k-1} \sum_{j=i+1}^{k-1} |c_i-c_j|\right) i=0k1j=i+1k1(rirj+cicj)=(i=0k1j=i+1k1rirj)+(i=0k1j=i+1k1cicj).

下面讨论如何计算第一个和式,第二个同理.设 s [ ] s[] s[] r [ ] r[] r[]非降序排列的后的结果,

​ 则 ∑ i = 0 k − 1 ∑ j = i + 1 k − 1 ∣ r i − r j ∣ = ∑ i = 0 k − 1 ∑ j = i + 1 k − 1 ( s j − s i ) = ( ∑ i = 0 k − 1 ∑ j = i + 1 k − 1 s j ) + ( ∑ i = 0 k − 1 ∑ j = i + 1 k − 1 − s i ) \displaystyle \sum_{i=0}^{k-1}\sum_{j=i+1}^{k-1} |r_i-r_j|=\sum_{i=0}^{k-1}\sum_{j=i+1}^{k-1}(s_j-s_i)=\left(\sum_{i=0}^{k-1}\sum_{j=i+1}^{k-1} s_j\right)+\left(\sum_{i=0}^{k-1}\sum_{j=i+1}^{k-1} -s_i\right) i=0k1j=i+1k1rirj=i=0k1j=i+1k1(sjsi)=(i=0k1j=i+1k1sj)+(i=0k1j=i+1k1si).

注意到第一个和式中 s j s_j sj出现 j j j次,第二个和式中 − s i -s_i si出现 ( k − i − 1 ) (k-i-1) (ki1)次,

​ 则上式 = ∑ j = 0 k − 1 j s j + ∑ i = 0 k − 1 − ( k − i − 1 ) s i = ∑ i = 0 k − 1 ( 2 i − k + 1 ) s i \displaystyle =\sum_{j=0}^{k-1} js_j+\sum_{i=0}^{k-1} -(k-i-1)s_i=\sum_{i=0}^{k-1} (2i-k+1)s_i =j=0k1jsj+i=0k1(ki1)si=i=0k1(2ik+1)si.

代码I

const int MAXN = 1e5 + 5;
int n, m;
vi row[MAXN], col[MAXN];  // 每种颜色所在的行、列

void solve() {
  cin >> n >> m;
  for (int i = 1; i <= n; i++) {
    for (int j = 1; j <= m; j++) {
      int c; cin >> c;
      row[c].push_back(i), col[c].push_back(j);
    }
  }

  for (int c = 1; c < MAXN; c++) sort(all(row[c])), sort(all(col[c]));

  ll ans = 0;
  for (int c = 1; c < MAXN; c++) {  // 枚举颜色
    int k = row[c].size();
    for (int i = 0; i <= k - 1; i++) ans += (ll)(2 * i - k + 1) * row[c][i];
    for (int i = 0; i <= k - 1; i++) ans += (ll)(2 * i - k + 1) * col[c][i];
  }
  cout << ans;
}

int main() {
  solve();
}

思路II

注意到每个 r i r_i ri对答案的贡献为 r i − r 1 + r i − r 2 + ⋯ + r i − r i − 1 = ( i − 1 ) r i − ( r 1 + ⋯ + r i − 1 ) r_i-r_1+r_i-r_2+\cdots+r_i-r_{i-1}=(i-1)r_i-(r_1+\cdots+r_{i-1}) rir1+rir2++riri1=(i1)ri(r1++ri1),用前缀和优化即可.

代码II

const int MAXN = 1e5 + 5;
int n, m;
vl row[MAXN], col[MAXN];  // 每种颜色所在的行、列
vl pre(MAXN);  // 前缀和

void solve() {
  cin >> n >> m;
  for (int i = 1; i <= n; i++) {
    for (int j = 1; j <= m; j++) {
      int c; cin >> c;
      row[c].push_back(i), col[c].push_back(j);
    }
  }

  ll ans = 0;
  for (int c = 1; c < MAXN; c++) {  // 枚举颜色
    sort(all(row[c]));
    partial_sum(all(row[c]), pre.begin());  // 注意用此函数时原数组也要开ll
    for (int i = 1; i < row[c].size(); i++) {
      ans += (ll)row[c][i] * i;  // 下标从0开始,故此处无需-1
      ans -= pre[i - 1];
    }

    sort(all(col[c]));
    partial_sum(all(col[c]), pre.begin());
    for (int i = 1; i < col[c].size(); i++) {
      ans += (ll)col[c][i] * i;  // 下标从0开始,故此处无需-1
      ans -= pre[i - 1];
    }
  }
  cout << ans;
}

int main() {
  solve();
}


1739C. Card Game

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

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

n n n( n n n为偶数)张牌,每张牌上写着一个 1 ∼ n 1\sim n 1n的整数,每张牌上的数字相异.称写着数字 x x x的牌大于写着数字 y y y的牌,如果 x > y x>y x>y.A和B玩游戏,A先手.初始时两人各自拿到 n 2 \dfrac{n}{2} 2n张牌.每轮每人打出一张牌,此时若对手能打出一张更大的牌,则游戏继续;否则对手失败.双方都能打完手中的牌时游戏平局.给定偶数 n n n,两人都采取最优策略,分别求使得A胜、B胜、平均的初始时的分牌方式,答案对 998244353 998244353 998244353取模.

t    ( 1 ≤ t ≤ 30 ) t\ \ (1\leq t\leq 30) t  (1t30)组测试数据.每组测试数据输入一个偶数 n    ( 2 ≤ n ≤ 60 ) n\ \ (2\leq n\leq 60) n  (2n60).

思路I

平局只有一种情况:

(1)若A有值为 n n n的牌,则他初始时打出即获胜.故平局时值为 n n n的牌只能属于B.

(2)若B有值为 ( n − 1 ) (n-1) (n1)的牌,则打第一次打出值为 n n n的牌,第二次打出值为 ( n − 1 ) (n-1) (n1)的牌即获胜.

​ 故平局时值为 ( n − 1 ) (n-1) (n1)的牌只能属于A.

(3)若B有值为 ( n − 2 ) (n-2) (n2)的牌,

​ ①若A第一次打值为 ( n − 1 ) (n-1) (n1)的牌,则B先打值为 n n n的牌,再打值为 ( n − 2 ) (n-2) (n2)的牌即获胜.

​ ②若A第一次打其他牌,则B先打值为 ( n − 2 ) (n-2) (n2)的牌,再打值为 n n n的牌即获胜.

​ 故要平局时值为 ( n − 2 ) (n-2) (n2)的牌只能属于A.

用一个长度为 n n n的字符串 s s s表示值为 n ∼ 1 n\sim 1 n1的牌的分配方式则平局当且仅当 s = B A A B B A A B B ⋯ s=BAABBAABB\cdots s=BAABBAABB.

设先手胜的方案数为 a n s ans ans,则后手胜的方案数为 C n n 2 − a n s − 1 C_n^{\frac{n}{2}}-ans-1 Cn2nans1.

下面求先手胜的方案数.由平局的分析过程知:应从大到小、每两个一组地考虑 n ∼ 1 n\sim 1 n1的整数.

①若先手有当前最大的牌,则先手必胜,先手获胜的方案数增加 C n 2 − 1 − n − i 2 i − 1 C_{\frac{n}{2}-1-\frac{n-i}{2}}^{i-1} C2n12nii1,

​ 即从剩下的牌中任选 ( i − 1 ) (i-1) (i1)张作为先手的其他牌.

②若后手同时有当前最大的牌和次大的牌,则后手必胜,后手获胜方案数增加 C n 2 − n − i 2 i − 2 C_{\frac{n}{2}-\frac{n-i}{2}}^{i-2} C2n2nii2,

​ 即从剩下的牌中任选 ( i − 2 ) (i-2) (i2)张作为后手的其他牌.

③若先手有当前的次大牌,后手有当前的最大牌,最优策略是先手先打次大的牌,后手再打最大的牌,只有一种情况.

从大到小每两个一组的枚举 n ∼ 1 n\sim 1 n1,切换先手计算答案即可.

代码I

const int MAXN = 65;
const int MOD = 998244353;
int C[MAXN][MAXN];  // 组合数C(n,m)

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() {
  int n; cin >> n;

  int ans1 = 0;  // 先手胜的方案数
  for (int i = n, cur = 0; i >= 1; i -= 2, cur ^= 1) {  // cur表示当前轮到的玩家,0为先手,1为后手
    if (cur) ans1 = (ans1 + C[i - 2][n / 2 - (n - i) / 2]) % MOD;
    else ans1 = (ans1 + C[i - 1][n / 2 - 1 - (n - i) / 2]) % MOD;
  }
  int ans2 = (C[n][n / 2] - ans1 - 1 + MOD) % MOD;  // 后手胜的方案数
  cout << ans1 << ' ' << ans2 << ' ' << 1 << endl;
}

int main() {
  init();
  CaseT  // 单测时注释掉该行
    solve();
}

思路II

思路I,用一个字符串 s s s表示分配情况,平局当且仅当 s ′ = B A A B B A A B B ⋯ s'=BAABBAABB\cdots s=BAABBAABB.

d p [ x ] [ y ] [ t ] dp[x][y][t] dp[x][y][t]表示 s s s中有 x x x个A、 y y y个B,若当前 s s s的字符与 s ′ s' s的对应字符相同,则 t = 0 t=0 t=0;若当前 s s s的字符与 s ′ s' s的对应字符不同,且这会导致A胜,则 t = 1 t=1 t=1;否则导致B胜,则 t = 2 t=2 t=2.最终答案分别为 d p [ n 2 ] [ n 2 ] [ t ]    ( t = 0 , 1 , 2 ) dp\left[\dfrac{n}{2}\right]\left[\dfrac{n}{2}\right][t]\ \ (t=0,1,2) dp[2n][2n][t]  (t=0,1,2),初始条件 d p [ 0 ] [ 0 ] [ 2 ] = 1 dp[0][0][2]=1 dp[0][0][2]=1,方便加入第一张牌后确定A是否必胜.

代码II

const int MAXN = 65;
const int MOD = 998244353;
int dp[MAXN][MAXN][3];  // dp[x][y][t]表示s中有x个A、y个B,
// 若当前s的字符与s'的对应字符相同,则t=0
// 若当前s的字符与s'的对应字符不同,且这会导致A胜,则t=1
// 若当前s的字符与s'的对应字符不同,且这会导致B胜,则t=2

void solve() {
  memset(dp, 0, so(dp));

  int n; cin >> n;

  dp[0][0][2] = 1;  // 初始条件B必胜
  for (int i = 0; i <= n / 2; i++) {
    for (int j = 0; j <= n / 2; j++) {
      for (int t = 0; t < 3; t++) {
        int cur = (i + j) % 4;  // 当前轮到的玩家
        if (cur == 0 || cur == 3) {
          if (i < n / 2) 
            dp[i + 1][j][t == 2 ? 0 : t] = (dp[i + 1][j][t == 2 ? 0 : t] + dp[i][j][t]) % MOD;  // 当前字符为A
          if (j < n / 2) 
            dp[i][j + 1][t] = (dp[i][j + 1][t] + dp[i][j][t]) % MOD;  // 当前字符为B
        }
        else {
          if (i < n / 2) 
            dp[i + 1][j][t] = (dp[i + 1][j][t] + dp[i][j][t]) % MOD;  // 当前字符为A
          if (j < n / 2) 
            dp[i][j + 1][t == 2 ? 1 : t] = (dp[i][j + 1][t == 2 ? 1 : t] + dp[i][j][t]) % MOD;  // 当前字符为B
        }
      }
    }
  }

  for (int i = 0; i < 3; i++) cout << dp[n / 2][n / 2][i] << " \n"[i == 2];
}

int main() {
  CaseT  // 单测时注释掉该行
    solve();
}


截至2022.10.18,无新题目.

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值