topcoder srm 460 div1
problem1 link
设$f[i][j]$表示已经分配了answers中的前$i$个,分配给的问题的状态为 $j$的方案数。
其中状态可以用$n$位的三进制表示,0表示还未分配,1表示已分配是 Yes,2表示已分配是No.
problem2 link
假设$n$个城市为[0,n-1]。设$f[i][j][t]$表示在[0,i]个城市中已经选择了$j$个城市,选择了$t$个人的概率。
那么选择第$i+1$个城市的概率是$\frac{k-j}{n-i-1}$.
problem3 link
首先,如果一个连通图中存在两个环,也就是有$n$个顶点,$n+1$条边,那么从其中一个环上的某一点(这一点不属于另外一个环)到另一个环上的某一点(同样这一点也不属于前一个环)会有超过两条简单路径。所以满足条件的连通图要么是树,要么是只有一个环(也就是树上多一条边)。
假设给出的边使得$n$个顶点分成了$m$个连通分量,第$i$个连通分量中点的个数是$v_{i}$。
(1)假设最后是一棵树(前提是$m$个连通分量中每个连通分量中都没有环)。最后的答案是$T(m)=n^{m-2}\prod_{i=1}^{m}v_{i}$.这里要用到prufer编码。对于$m$个联通块的树,其prufer编码的长度为$m-2$。每个位置有$n$种情况,最后乘以叶子节点可以连出的边的数量。
比如$m=3,n=4$,分别是1-2,3,4。那么长度为1的prufer编码可以是{1},{2},{3},{4}.(推导过程参看上面的链接)
对于{1}来说,有两种:第一种: 1-3,1-4, 第二种1-3,2-4
对于{2}来说,有两种:第一种: 2-3,2-4, 第二种2-3,1-4
对于{3}来说,有两种:第一种: 3-1,3-4, 第二种3-2,3-4
对于{4}来说,有两种:第一种: 4-1,4-3, 第二种4-2,4-3
(2)有一个环。那么就要看这个环连通了$m$个联通块中的几个。
第一种: 1个,那么只需要枚举是第几个联通块;
第二种:2个,假设是联通块$a,b$.那么就是$C_{v_av_b}^{2}$
第三种:大于等于3个,假设是$k$个,那么是$\frac{(k-1)!}{2}\prod_{i=1}^{k}v_{ci}^{2}$,其中$ci$第第$i$个选出的联通块的编号。这里除以2是因为有翻转导致重复的情况。比如$k=4$时,$abcd$和$dcab$是一样的。
最后将连成的环看做一个连通分量,那么最后还有$m-k+1$个连通分量,将其连成一个树的方案为$T(m-k+1)$。
code for problem1
import java.util.*; import java.math.*; import static java.lang.Math.*; public class TheQuestionsAndAnswersDivOne { public int find(int n, String[] answers) { int[] p = new int[n + 2]; p[0] = 1; for (int i = 1; i < p.length; ++ i) { p[i] = p[i - 1] * 3; } int[][] f = new int[answers.length][p[n]]; for (int i = 0; i < n; ++ i) { int t = answers[0].equals("No")? 1 : 2; f[0][t * p[i]] = 1; } for (int i = 1; i < answers.length; ++ i) { int t = answers[i].equals("No")? 1 : 2; for (int k = 0; k < p[n]; ++ k) { if (f[i - 1][k] == 0) { continue; } for (int j = 0; j < n; ++ j) { int x = k / p[j] % 3; if (x == 0 || x == t) { int nk = k; if (x == 0) { nk += t * p[j]; } f[i][nk] += f[i - 1][k]; } } } } int result = 0; for (int i = 0; i < p[n]; ++ i) { boolean tag = true; for (int j = 0; j < n; ++ j) { if (i / p[j] % 3 == 0) { tag = false; break; } } if (tag) { result += f[answers.length - 1][i]; } } return result; } }
code for problem2
import java.util.*; import java.math.*; import static java.lang.Math.*; public class TheFansAndMeetingsDivOne { public double find(int[] minJ, int[] maxJ, int[] minB, int[] maxB, int k) { final int n = minJ.length; int s1 = 0, s2 = 0; for (int i = 0; i < n; ++ i) { s1 += maxJ[i]; s2 += maxB[i]; } final int m = Math.min(s1, s2) + 1; double[][][] f = new double[n][k + 1][m]; double[][][] g = new double[n][k + 1][m]; f[0][0][0] = 1 - 1.0 * k / n; for (int i = minJ[0]; i <= maxJ[0] && i < m; ++ i) { f[0][1][i] = 1.0 * k / n / (maxJ[0] - minJ[0] + 1); } g[0][0][0] = 1 - 1.0 * k / n; for (int i = minB[0]; i <= maxB[0] && i < m; ++ i) { g[0][1][i] = 1.0 * k / n / (maxB[0] - minB[0] + 1); } for (int i = 1; i < n; ++ i) { for (int j = 0; j <= i && j <= k; ++ j) { final double p = 1.0 * (k - j) / (n - i); final double p1 = p / (maxJ[i] - minJ[i] + 1); final double p2 = p / (maxB[i] - minB[i] + 1); for (int t = 0; t < m; ++ t) { f[i][j][t] += f[i - 1][j][t] * (1 - p); g[i][j][t] += g[i - 1][j][t] * (1 - p); if (j == k) { continue; } for (int x = minJ[i]; x <= maxJ[i] && x + t < m; ++ x) { f[i][j + 1][x + t] += f[i - 1][j][t] * p1; } for (int x = minB[i]; x <= maxB[i] && x + t < m; ++ x) { g[i][j + 1][x + t] += g[i - 1][j][t] * p2; } } } } double result = 0; for (int i = 0; i < m; ++ i) { result += f[n - 1][k][i] * g[n - 1][k][i]; } return result; } }
code for problem3
import java.util.*; import java.math.*; import static java.lang.Math.*; public class TheCitiesAndRoadsDivOne { static final int MOD = 1234567891; public int find(String[] map) { final int n = map.length; UnionSet unionSet = new UnionSet(n); for (int i = 0; i < n; ++ i) { for (int j = i + 1; j < n; ++ j) { if (map[i].charAt(j) == 'Y') { unionSet.merge(i, j); } } } List<Integer> list = new ArrayList<>(); int m = 0; int circleNum = 0; for (int i = 0; i < n; ++ i) { if (unionSet.get(i) == i) { ++ m; list.add(unionSet.vertexNum[i]); circleNum += unionSet.edges[i] - unionSet.vertexNum[i] + 1; } } if (circleNum > 1) { return 0; } if (m == 1) { if (circleNum == 1 || list.get(0) <= 2) { return 1; } return list.get(0) * (list.get(0) - 1) / 2 - (list.get(0) - 1) + 1; } long[] p = new long[n + 1]; p[0] = 1; for (int i = 1; i <= n; ++ i) { p[i] = p[i-1] * n % MOD; } long result = p[m - 2]; for (int i = 0; i < m; ++ i) { result = result * list.get(i) % MOD; } if (circleNum == 1) { return (int)result; } int sum = 0; for (int i = 0; i < m; ++ i) { int t = list.get(i); sum += t * (t - 1) / 2 - (t - 1); } result = result * (sum + 1) % MOD; for (int i = 0; i < (1 << m); ++ i) { int cnt = 0; for (int j = 0; j < m; ++ j) { if ((i & (1 << j)) != 0) { ++ cnt; } } if (cnt < 2) { continue; } long tmp = 1; if (cnt == 2) { for (int j = 0; j < m; ++ j) { if ((i & (1 << j)) != 0) { tmp *= list.get(j); } } tmp = tmp * (tmp - 1) / 2; } else { for (int j = 3; j <= cnt - 1; ++ j) { tmp = tmp * j % MOD; } for (int j = 0; j < m; ++ j) { if ((i & (1 << j)) != 0) { tmp = tmp * list.get(j) * list.get(j) % MOD; } } } if (cnt == m) { result = (result + tmp) % MOD; continue; } tmp = tmp * p[(m - cnt + 1) - 2] % MOD; long totVertex = 0; for (int j = 0; j < m; ++ j) { if ((i & (1 << j)) != 0) { totVertex += list.get(j); } else { tmp = tmp * list.get(j) % MOD; } } tmp = tmp * totVertex % MOD; result = (result + tmp) % MOD; } return (int)result; } static class UnionSet { int[] father = null; int[] edges = null; int[] vertexNum = null; UnionSet(int n) { father = new int[n]; edges = new int[n]; vertexNum = new int[n]; for (int i = 0; i < n; ++ i) { father[i] = i; edges[i] = 0; vertexNum[i] = 1; } } int get(int u) { if (father[u] == u) { return u; } return father[u] = get(father[u]); } void merge(int u, int v) { int fu = get(u); int fv = get(v); ++ edges[fu]; if (fu == fv) { return; } father[fv] = fu; vertexNum[fu] += vertexNum[fv]; edges[fu] += edges[fv]; } } }