A
模拟 找到两个相邻的O变成X。
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 char s[1005][10]; 5 int main () { 6 bool ok = false; 7 int n; scanf("%d", &n); 8 for(int i = 1; i <= n; ++ i) { 9 scanf("%s", s[i] + 1); 10 if(!ok && s[i][1] == 'O' && s[i][2] == 'O') ok = true, s[i][1] = s[i][2] = '+'; 11 if(!ok && s[i][4] == 'O' && s[i][5] == 'O') ok = true, s[i][4] = s[i][5] = '+'; 12 } 13 if(!ok) puts("NO"); 14 else { 15 puts("YES"); 16 for(int i = 1; i <= n; ++ i) puts(s[i] + 1); 17 } 18 return 0; 19 }
B
题意:给一个n×n的矩阵,其中每个矩阵有且只有一个数是0,其他都是大于零的数,问能否在这个值为0的位置填上一个大于0的数使得矩阵的每行、每列和对角线的和都相等
思路:按一维找到这个数后在判断其他维是否满足。(注意要用long long)
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 long long a[505][505]; 5 pair<long long, int>sum1[505], sum2[505]; 6 int main () { 7 int n; scanf("%d", &n); 8 for(int i = 1; i <= n; ++ i) { 9 for(int j = 1; j <= n; ++ j) { 10 scanf("%lld", &a[i][j]); 11 sum1[i].first += a[i][j], sum1[i].second = i; 12 sum2[j].first += a[i][j], sum2[j].second = j; 13 } 14 } 15 if(n == 1) return 0 * puts("1"); 16 sort(sum1 + 1, sum1 + 1 + n), sort(sum2 + 1, sum2 + 1 + n); 17 if(sum1[1].first >= sum1[2].first || sum1[2].first != sum1[n].first) return 0 * puts("-1"); 18 if(sum2[1].first >= sum2[2].first || sum2[2].first != sum2[n].first) return 0 * puts("-1"); 19 if(sum1[1].first != sum2[1].first || sum1[2].first != sum2[2].first) return 0 * puts("-1"); 20 long long ans = sum1[2].first - sum1[1].first; 21 a[sum1[1].second][sum2[1].second] = ans; 22 long long x = 0, y = 0; 23 for(int i = 1; i <= n; ++ i) x += a[i][i], y += a[i][n - i + 1]; 24 if(x != y || x != sum1[2].first) return 0 * puts("-1"); 25 printf("%lld ", ans); 26 return 0; 27 }
C
题意:n棵树,m种颜色,这些树有些已经涂了颜色有些没涂,可以花费p[i][j]的价值给没有涂色的第i棵树涂上第j种颜色,问最终把树涂成恰好k段花费的最小价值,(相同颜色为一段)
思路: dp[i][j][k]表示涂到第i棵树,用的颜料是第j种,当前被分成了k段的最小价值,暴力转移下。
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 int color[105]; 5 long long p[105][105]; 6 long long dp[105][105][105]; 7 //pos 8 //select 9 //num 10 11 void update(long long &x, long long y) { 12 if(x == -1) x = y; 13 else if(x > y) x = y; 14 } 15 16 int main () { 17 int n, m, K; scanf("%d%d%d", &n, &m, &K); 18 for(int i = 1; i <= n; ++ i) scanf("%d", color + i); 19 for(int i = 1; i <= n; ++ i) for(int j = 1; j <= m; ++ j) scanf("%lld", &p[i][j]); 20 memset(dp, -1, sizeof dp), dp[0][0][0] = 0; 21 for(int i = 0; i <= n - 1; ++ i) { 22 for(int j = 0; j <= m; ++ j) { 23 for(int k = 0; k <= K; ++ k) if(dp[i][j][k] != -1) { 24 for(int l = 1; l <= m; ++ l) { 25 if(color[i + 1] != 0) { 26 if(l == color[i + 1]) { 27 update(dp[i + 1][l][k + (j == l ? 0 : 1)], dp[i][j][k] + p[i + 1][l]); 28 } 29 } else { 30 update(dp[i + 1][l][k + (j == l ? 0 : 1)], dp[i][j][k] + p[i + 1][l]); 31 } 32 } 33 } 34 } 35 } 36 long long ans = 1LL << 50; 37 for(int i = 1; i <= m; ++ i) if(dp[n][i][K] != -1) ans = min(ans, dp[n][i][K]); 38 if(ans == 1LL << 50) return 0 * puts("-1"); 39 else { 40 for(int i = 1; i <= n; ++ i) if(color[i]) ans -= p[i][color[i]]; 41 printf("%lld ", ans); 42 } 43 return 0; 44 }
D
题意:n个点,每个点对a[i](a[i] != i)这个点有一条单向边,问选择一些边,把它们转向后这个图没有环的方案数。
思路:如果x个点是一个环,那么总共有2^x种选择方案,其中都选和都不选不符合要求,那么有2^x - 2种方案可行,如果一条边不属于某条环,那么转不转这条边都不会成环,有2种方案可行。求出环后把方案数乘起来就是答案。
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 const int mod = 1e9 + 7; 5 6 int p[200005], p2[200005], cnt[200005]; 7 8 int dfn[200005], low[200005], belong[200005], scc, bnum; 9 10 stack<int>sta; 11 void tarjan(int u) { 12 dfn[u] = low[u] = ++ scc; 13 sta.push(u); 14 if(dfn[p[u]] == 0) tarjan(p[u]), low[u] = min(low[u], low[p[u]]); 15 else if(!belong[p[u]]) low[u] = min(low[u], dfn[p[u]]); 16 if(low[u] == dfn[u]) { 17 ++ bnum; 18 int v; 19 do { 20 v = sta.top(); sta.pop(); 21 belong[v] = bnum; 22 } while(u != v); 23 } 24 } 25 26 int main () { 27 p2[0] = 1; 28 for(int i = 1; i <= 200000; ++ i) p2[i] = p2[i - 1] * 2 % mod; 29 int n; scanf("%d", &n); 30 for(int i = 1; i <= n; ++ i) scanf("%d", p + i); 31 for(int i = 1; i <= n; ++ i) if(!belong[i]) tarjan(i); 32 for(int i = 1; i <= n; ++ i) cnt[belong[i]] ++; 33 int ans = 1; 34 for(int i = 1; i <= n; ++ i) { 35 if(cnt[i] == 1) ans = ans * 2 % mod; 36 else if(cnt[i] > 1) ans = 1LL * ans * (p2[cnt[i]] + mod - 2) % mod; 37 } 38 printf("%d ", ans); 39 return 0; 40 }
E
题意:一个地方一年有2^n天,问k个人至少有两个人是同一天生日的概率。最后以最简分数的形式表达。数字太大,那么在对1000003取余输出。
思路:如果2^n < m,根据鸽巢原理,答案为1/1,否则答案为1 - 2^n *(2^n-1)*……*(2^n-(m-1))/ 2^nm,分母好算,分子的话注意到mod=1e6+3,连续1e6+3个数相乘取余即为0,那么暴力算。算最简分数时发现分母只有2这个因子,于是去找分母中这个因子,然后分子分母求个逆元。
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 const int mod = 1e6 + 3; 5 6 long long fp(long long a, long long b) { 7 long long res = 1; 8 while(b) { 9 if(b & 1) res = res * a % mod; 10 if(b >>= 1) a = a * a % mod; 11 } 12 return res; 13 } 14 15 long long cal(long long n, long long k) { 16 long long res = 1, x = fp(2, n % ( mod - 1)); 17 for(long long i = 0; i <= k - 1; ++ i) { 18 res = res * (x - i + mod) % mod; 19 if(res == 0) return 0; 20 } 21 return res; 22 } 23 24 int main () { 25 long long n, k; scanf("%lld%lld", &n, &k); 26 if(n <= 60 && (1LL << n) < k) return 0 * puts("1 1"); 27 if(k == 1) return 0 * puts("0 1"); 28 long long A = cal(n, k); 29 long long B = fp(2, (n % (mod - 1)) * (k % (mod - 1)) % (mod - 1)); 30 long long x = n; -- k; 31 while(k) k /= 2, x += k; 32 x = fp(2, x % (mod - 1)), A = A * fp(x, mod - 2) % mod, B = B * fp(x, mod - 2) % mod; 33 printf("%lld %lld ", (B - A + mod) % mod, B % mod); 34 return 0; 35 }