一些经典的容斥问题
- 在平面中给$n$个点,求这$n$个点构成的三角形/锐角三角形的个数。
求三角形的个数比较简单。首先全集是$\binom{n}{3}$,然后考虑补集,补集就是三点共线的点对。所以我们可以枚举每一个点,然后为了避免算重,我们接下来只考虑标号比当前点小的点。接着就进行极角排序,这样就可以统计出当前点所在的所有直线以及直线上的点的个数。设某直线上有$m$个点,那么答案就减去$\binom{m}{3}$即可。注意处理重点的情况。
求锐角三角形的个数也比较简单,只不过补集除了三点共线之外,还会有钝角三角形和直角三角形。然而我们还是可以考虑枚举每一个点$u$,只不过这里不需要只考虑标号比当前点小的点了,因为接下来的算法不会算重。然后也还是极角排序,然后按照极角序枚举另一个点$v$,这样就构成了一条线段$u-v$。然后过点$u$作线段$u-v$的垂线,那么在垂线另一边或者正好在垂线上的点都和$u,v$构成非锐角三角形或直线,统计这样的点的个数即可,利用单调性可以做到$O(n)$求解。然后每个非锐角三角形或直线会被算两次,所以最后再除以2就行了。
至于题目的话,就写个正解写个暴力拍一拍吧。反正你们这么强,分分钟就可以写完。
- 给一个环,环上有$n$个点。现在你有$m$种颜色,然后你要给每个点染一种颜色,并使得最后所有相邻的点的颜色都不相同,问有多少种染色方案。
首先考虑全集,就是$m\times (m-1)^{n-1}$。意思就是第一个点任选颜色,后面的点任选和上个点不同的颜色。听起来很靠谱,但是最后一个就可能和第一个变成一样的颜色。所以我们要减去补集,就是最后一个和第一个颜色相同的情况的种数,即$m\times (m-1)^{n-2}$。然后发现又减多了,把倒数第二个和倒数第一个颜色相同的情况也给减掉了,实际上这是不能减的,所以我们要加回来,即加上$m\times (m-1)^{n-3}$。就这样一直反复,直到最后没有后续状态了为止。所以有:
$$Ans=m\times(m-1)^{n-1}-m\times(m-1)^{n-2}+m\times(m-1)^{n-3}-m\times(m-1)^{n-4}+...+m\times(-1)^{n-1}$$
最后用等比公式求和即可简化计算。
再给一个类似的题吧。http://codeforces.com/gym/100548/attachments F题
- 给一个$n\times m$的网格,你一开始在$(1,1)$上,然后要走到$(n,m)$,每次可以往右走或者往下走一格,但是不能出界。然而在网格中有$k$个障碍点,你不能走到障碍点上,问有多少种从$(1,1)$走到$(n,m)$的走法。
首先全集就是$\binom{n+m-2}{n-1}$,要走$n+m-2$步,其中有$n-1$步是往下,所以总方案数就是$\binom{n+m-2}{n-1}$。做这类题目的关键因素之一就是能够很快的算出或者预处理出一个子矩阵的全集。然后我们考虑补集,我们考虑只经过了一个障碍点的路径,假设该障碍点是$(u,v)$,那么所有经过该障碍点的路径条数即为$S(u,v)\times S(n-u+1,m-v+1)$,其中$S(x,y)$表示从$(1,1)$走到$(x,y)$的放案数,即$\binom{x+u-2}{x-1}$。然后将其减去。但是我们再来考虑那些经过了两个障碍点的路径,这些路径被减去了两次,所以我们要加回来。然后又要减去经过了三个障碍的,加上经过了四个障碍的......所以我们可以考虑给障碍点之间连边。$(u,v)$给$(p,q)$连边当且仅当$u\le p$且$v\le q$且$u\neq p,v\neq q$。但是在这样的图中链是很多的,所以我们可以考虑动态规划。设$Dp[i][j]$表示从$(1,1)$开始,以$j$结尾的包含了$i$个障碍点的路径条数,转移就枚举$j$的每一个后继$u$,令$Dp[i+1][u]+=Dp[i][j]\times S(x_u-x_j+1,y_u-y_j+1)$,然后$Dp[i][j]$对答案的贡献就是$Dp[i][j]\times S(n-x_j+1,m-y_j+1)\times (-1)^i$,累加贡献即可。复杂度是$O(Calc_S\times k^3)$的,其中$Calc_S$是计算$S$的时间复杂度。
给一个类似的题:http://acm.hdu.edu.cn/showproblem.php?pid=5794。
我把我的代码贴上来吧,仅供参考。
1 #include <map> 2 #include <ctime> 3 #include <cstdio> 4 #include <vector> 5 #include <algorithm> 6 using namespace std; 7 typedef long long LL; 8 #define N 100 + 5 9 #define Mod 110119 10 11 int Case, r, ans, Fac[Mod], Inv[Mod], Ord[N], TMP[N][N], W[N][N]; 12 bool Ok[N], Inq[N]; 13 LL n, m, X[N], Y[N], _X[N], _Y[N]; 14 vector <int> To[N], Vec[N]; 15 16 inline int Inc(int a, int b) 17 { 18 return a + b - (a + b >= Mod ? Mod : 0); 19 } 20 21 inline bool cmp(int u, int v) 22 { 23 return make_pair(X[u], Y[u]) < make_pair(X[v], Y[v]); 24 } 25 26 inline int Power(int u, int v) 27 { 28 int res = 1; 29 for (; v; v >>= 1) 30 { 31 if (v & 1) res = (LL) res * u % Mod; 32 u = (LL) u * u % Mod; 33 } 34 return res; 35 } 36 37 inline void Prepare() 38 { 39 Fac[0] = Inv[0] = 1; 40 for (int i = 1; i < Mod; i ++) 41 Fac[i] = (LL) Fac[i - 1] * i % Mod; 42 Inv[Mod - 1] = Power(Fac[Mod - 1], Mod - 2); 43 for (int i = Mod - 2; i; i --) 44 Inv[i] = (LL) Inv[i + 1] * (i + 1) % Mod; 45 } 46 47 inline int C(int x, int y) 48 { 49 if (x < y) return 0; 50 return (LL) Fac[x] * Inv[y] % Mod * Inv[x - y] % Mod; 51 } 52 53 inline int Com(LL x, LL y) 54 { 55 if (x < y || x < 0 || y < 0) return 0; 56 if (y == 0) return 1; 57 if (x < Mod && y < Mod) return C(x, y); 58 return (LL) C(x % Mod, y % Mod) * Com(x / Mod, y / Mod) % Mod; 59 } 60 61 inline int Get(LL u, LL v) 62 { 63 if ((u + v - 2) % 3 != 0) return 0; 64 LL t = (u + v - 2) / 3; 65 LL x = u - 1 - t, y = v - 1 - t; 66 int res = Com(x + y, x); 67 return res; 68 } 69 70 inline int Get_1(int u, int v) 71 { 72 if (TMP[u][v] != -1) return TMP[u][v]; 73 return TMP[u][v] = Get(X[v] - X[u] + 1, Y[v] - Y[u] + 1); 74 } 75 76 inline void Handle() 77 { 78 ans = Get_1(0, r + 1); 79 for (int i = 1; i <= r; i ++) 80 { 81 if (!Ok[i]) continue ; 82 Vec[1].push_back(i); 83 W[1][i] = Get_1(0, i); 84 } 85 for (int k = 1; k <= r; k ++) 86 { 87 for (int i = 1; i <= r; i ++) Inq[i] = 0; 88 for (int i = 0; i < Vec[k].size(); i ++) 89 { 90 int j = Vec[k][i]; 91 int tmp = (LL) W[k][j] * Get_1(j, r + 1) % Mod; 92 if (k & 1) tmp = Mod - tmp; 93 ans = Inc(ans, tmp); 94 for (int l = 0; l < To[j].size(); l ++) 95 { 96 int d = To[j][l]; 97 if (!Ok[d]) continue ; 98 if (!Inq[d]) Vec[k + 1].push_back(d), Inq[d] = 1; 99 W[k + 1][d] = Inc(W[k + 1][d], (LL) W[k][j] * Get_1(j, d) % Mod); 100 } 101 } 102 } 103 } 104 105 int main() 106 { 107 Prepare(); 108 while (scanf("%lld%lld%d", &n, &m, &r) > 0) 109 { 110 X[0] = Y[0] = 1, Ok[0] = 1; 111 bool ok = 1; 112 for (int i = 1; i <= r; i ++) 113 { 114 scanf("%lld%lld", X + i, Y + i); 115 if (X[i] == n && Y[i] == m) ok = 0; 116 Ord[i] = i, Ok[i] = 1; 117 } 118 for (int i = 0; i <= r + 1; i ++) 119 for (int j = 0; j <= r + 1; j ++) 120 TMP[i][j] = -1, W[i][j] = 0; 121 sort(Ord + 1, Ord + r + 1, cmp); 122 for (int i = 1; i <= r; i ++) 123 _X[i] = X[Ord[i]], _Y[i] = Y[Ord[i]]; 124 for (int i = 1; i <= r; i ++) 125 { 126 X[i] = _X[i], Y[i] = _Y[i]; 127 if (X[i] == X[i - 1] && Y[i] == Y[i - 1]) Ok[i] = 0; 128 } 129 if (n == 1 && m == 1) 130 { 131 printf("Case #%d: %d\n", ++ Case, 1); 132 continue ; 133 } 134 if (!ok) 135 { 136 printf("Case #%d: %d\n", ++ Case, 0); 137 continue ; 138 } 139 X[r + 1] = n, Y[r + 1] = m, Ok[r + 1] = 1; 140 for (int i = 0; i <= r + 1; i ++) 141 To[i].clear(), Vec[i].clear(); 142 for (int i = 1; i < r; i ++) 143 for (int j = i + 1; j <= r; j ++) 144 if (X[j] > X[i] && Y[j] > Y[i] && Ok[j]) 145 To[i].push_back(j); 146 Handle(); 147 printf("Case #%d: %d\n", ++ Case, ans); 148 } 149 return 0; 150 }