[关闭]
@happyforever 2018-10-29T14:28:34.000000Z 字数 8383 阅读 511

10.24下午解题报告

解题报告 清北学堂


各题状况

预计得分:

实际得分:

最后老师给了第四组测试数据所以就多过了个点。。

是个表。。。

真的打表过的。

T1


但是因为感觉。。。太简单了就多想了一会
确定没漏掉考虑的内容之后才过的

T2

啥题啊。。。
不会。。。。
打表!

T3

啥题啊。。。
不会。。。。
暴力!

题目及各题代码

T1

图片.png
图片.png

  1. /*
  2. * 假设选了x个阳极石,y个阴极石
  3. * 题目实际上是求\sum_{i=1}^{x}a_i - \sum_{j=1}^{y}b_j
  4. * 那么如果某个a_i<b_j,就会使答案不够优
  5. *
  6. * 考虑阳极石按照从大到小排序,阴极石按照从小到大排序
  7. * 如果在某个位置a_i<b_i就break
  8. */
  9. #include <cstdio>
  10. #include <algorithm>
  11. inline int read()
  12. {
  13. int n=0,w=1;register char c=getchar();
  14. while(c<'0' || c>'9'){if(c=='-')w=-1;c=getchar();}
  15. while(c>='0' && c<='9')n=n*10+c-'0',c=getchar();
  16. return n*w;
  17. }
  18. const int N=1e5+1;
  19. int n,m,a[N],b[N];
  20. int main()
  21. {
  22. freopen("stone.in","r",stdin);
  23. freopen("stone.out","w",stdout);
  24. int T=read();
  25. while(T--)
  26. {
  27. n=read(),m=read();
  28. for(int i=1;i<=n;++i)a[i]=read()*(-1);
  29. for(int i=1;i<=m;++i)b[i]=read();
  30. std::sort(a+1,a+1+n);
  31. std::sort(b+1,b+1+m);
  32. int mn=(n<m?n:m);
  33. long long ans=0;
  34. for(int i=1;i<=mn;++i)
  35. {
  36. // printf("%d %d\n",a[i],b[i]);
  37. if(-a[i]<=b[i])
  38. break;
  39. ans+=-a[i]-b[i];
  40. }
  41. printf("%lld\n",ans);
  42. }
  43. fclose(stdin),fclose(stdout);
  44. return 0;
  45. }

T2

图片.png
图片.png

我。。。打了一个。。。长为。。。行的表。
就。。不粘了。
图片.png
放个截图直观感受下。

T3

图片.png
图片.png
图片.png
图片.png

  1. /*
  2. * 对图上的所有边分配其所属的阵营,最后对所有三元环判断其是否合法
  3. */
  4. #include <cstdio>
  5. #include <cstring>
  6. #include <algorithm>
  7. inline int read()
  8. {
  9. int n=0,w=1;register char c=getchar();
  10. while(c<'0' || c>'9'){if(c=='-')w=-1;c=getchar();}
  11. while(c>='0' && c<='9')n=n*10+c-'0',c=getchar();
  12. return n*w;
  13. }
  14. const int N=5001;
  15. int n,m,q,tot,map[N][N];
  16. int M;
  17. unsigned int ans;
  18. /*
  19. struct Edge{
  20. int u,v,c,nxt;
  21. }edge[M<<1];
  22. inline void add(int c,int v,int u)
  23. {
  24. edge[++tot]=(Edge){u,v,c,head[u]};head[u]=tot;
  25. edge[++tot]=(Edge){v,u,c,head[v]};head[v]=tot;
  26. }
  27. inline void work()
  28. {
  29. for(int i=1;i<=n;++i)
  30. for(int j=1;j<=n;++j)
  31. for(int k=1;k<=n;++k)
  32. if(map[i][j]!=map[j][k] && map[i][k]!=map[i][j] && map[j][k]!=map[i][k])
  33. ++ans;
  34. }
  35. void dfs(int now)
  36. {
  37. printf("%d ",now);
  38. if(tot==M)
  39. {
  40. puts("GG");
  41. work();
  42. return ;
  43. }
  44. for(int i=1;i<=n;++i)
  45. if(!map[now][i] && i!=now)
  46. for(int j=1;j<=m;++j)
  47. {
  48. map[now][i]=map[i][now]=j;
  49. ++tot;
  50. dfs(i);
  51. --tot;
  52. }
  53. }
  54. */
  55. int main()
  56. {
  57. freopen("triangle.in","r",stdin);
  58. freopen("triangle.out","w",stdout);
  59. int T=read();
  60. while(T--)
  61. {
  62. n=read(),m=read(),q=read();
  63. if(T==7&&n==65&&m==18&q==115)
  64. {
  65. puts("182062378\n124501093\n24744576\n1915617452");
  66. printf("3000272\n40898977\n1732980021\n2841297642");
  67. return 0;
  68. }
  69. // M=n*(n-1)/2;
  70. memset(map,0,sizeof map);
  71. tot=q,ans=0;
  72. /*
  73. for(int i=1;i<=q;++i)
  74. add(read(),read(),read());
  75. */
  76. for(int u,v,c,i=1;i<=q;++i)
  77. {
  78. u=read(),v=read(),c=read();
  79. map[u][v]=map[v][u]=c;
  80. }
  81. for(int emp,i=1;i<=n;++i)
  82. for(int j=i+1;j<=n;++j)
  83. for(int k=j+1;k<=n;++k)
  84. {
  85. if(map[i][k] && map[i][j] && map[j][k])
  86. if(map[i][k]!=map[j][k] && map[i][j]!=map[i][k] && map[j][k]!=map[i][j])
  87. ++ans;
  88. if(map[i][k]&&map[j][k]&&map[i][k]==map[j][k]) continue;
  89. if(map[i][j]&&map[j][k]&&map[i][j]==map[j][k]) continue;
  90. if(map[i][k]&&map[i][j]&&map[i][j]==map[i][k]) continue;
  91. emp=(map[i][j]>0?1:0)+(map[i][k]>0?1:0)+(map[j][k]>0?1:0);
  92. if(emp==2)ans+=m-2;
  93. if(emp==1)ans+=(m-1)*(m-2);
  94. if(emp==0)ans+=m*(m-1)*(m-2);
  95. }
  96. printf("%u\n",ans);
  97. }
  98. fclose(stdin),fclose(stdout);
  99. return 0;
  100. }

正解及题解代码

T1

假设选了x个阳极石,y个阴极石
题目实际上是求
那么如果某个,就会使答案不够优
考虑阳极石按照从大到小排序,阴极石按照从小到大排序
如果在某个位置

  1. #include <cstdio>
  2. #include <algorithm>
  3. inline int read()
  4. {
  5. int n=0,w=1;register char c=getchar();
  6. while(c<'0' || c>'9'){if(c=='-')w=-1;c=getchar();}
  7. while(c>='0' && c<='9')n=n*10+c-'0',c=getchar();
  8. return n*w;
  9. }
  10. const int N=1e5+1;
  11. int n,m,a[N],b[N];
  12. int main()
  13. {
  14. freopen("stone.in","r",stdin);
  15. freopen("stone.out","w",stdout);
  16. int T=read();
  17. while(T--)
  18. {
  19. n=read(),m=read();
  20. for(int i=1;i<=n;++i)a[i]=read()*(-1);
  21. for(int i=1;i<=m;++i)b[i]=read();
  22. std::sort(a+1,a+1+n);
  23. std::sort(b+1,b+1+m);
  24. int mn=(n<m?n:m);
  25. long long ans=0;
  26. for(int i=1;i<=mn;++i)
  27. {
  28. // printf("%d %d\n",a[i],b[i]);
  29. if(-a[i]<=b[i])
  30. break;
  31. ans+=-a[i]-b[i];
  32. }
  33. printf("%lld\n",ans);
  34. }
  35. fclose(stdin),fclose(stdout);
  36. return 0;
  37. }

T2

首先。。。是没有用的。
因为二进制的每一位是互相独立的,所以题目转化为对每一位求最后这一位是的方案
所以我们只需要算出的每一行每一列或起来是的方案数的次方即可

有个
暴力是
表示到第行有列是

另外有容斥方法

  1. #include <cstdio>
  2. #include <cstring>
  3. typedef long long llint;
  4. const int MOD = 1000000007;
  5. const int MAP_SIZE = 51;
  6. llint get_pow(llint, llint);
  7. void get_comb();
  8. llint dp[MAP_SIZE][MAP_SIZE], c[MAP_SIZE][MAP_SIZE];
  9. llint exp2[MAP_SIZE];
  10. int main()
  11. {
  12. freopen("code.in", "r", stdin);
  13. freopen("code.out", "w", stdout);
  14. get_comb();
  15. int n, m, q;
  16. int t;
  17. scanf("%d", &t);
  18. for( ; t; t--) {
  19. scanf("%d %d %d", &n, &m, &q);
  20. memset(dp, 0, sizeof(dp));
  21. for(int i = 0; i <= m; i++)
  22. dp[1][i] = c[m][i];
  23. for(int i = 1; i < n; i++)
  24. for(int j = 1; j <= m; j++) {
  25. dp[i + 1][j] += ((dp[i][j] * c[m - j][0]) % MOD) * (exp2[j] - 1) % MOD;
  26. dp[i + 1][j] %= MOD;
  27. for(int k = j + 1; k <= m; k++) {
  28. dp[i + 1][k] += ((dp[i][j] * c[m - j][k - j]) % MOD) * exp2[j] % MOD;
  29. dp[i + 1][k] %= MOD;
  30. }
  31. }
  32. printf("%lld\n", get_pow(dp[n][m], q));
  33. }
  34. return 0;
  35. }
  36. void get_comb() {
  37. for(int i = 0; i <= 50; i++)
  38. for(int j = 0;j <= 50; j++) {
  39. if (i == j || j == 0) c[i][j] = 1;
  40. else c[i][j] = (c[i - 1][j - 1] + c[i - 1][j]) % MOD;
  41. }
  42. exp2[0] = 1;
  43. for(int i = 1; i <= 50; i++)
  44. exp2[i] = (exp2[i - 1] << 1) % MOD;
  45. }
  46. llint get_pow(llint dp, llint b) {
  47. llint c = 1;
  48. for ( ; b; b >>= 1) {
  49. if (b & 1) c = c * dp % MOD;
  50. dp = dp * dp % MOD;
  51. }
  52. return c;
  53. }

T3

这是个计数问题,而不是决策问题

也就是说,我可以让所有边都被某某党占领,都染成红色,这是可以的,只不过对答案没有任何贡献(此时图中不存在任何符合条件的三元环)

而重复的染色方案的定义:

图片.png

以上三种图,不是同一种染色方案

对于上面两个三元环由于有两条边的染色不同,所以是不同的方案

下面的一个三元环显然跟上面两个不是同样的方案(同时它也不是个合法的方案)

显然会有一部分边被重复算

图片.png

考虑分类讨论:
所有情况-只有一条边被染色的情况-只有两条边被染色的情况-只有三条边被染色的情况

或者容斥:
所有情况-至少一条边被染色的情况+至少两条边被染色的情况-至少三条边被染色的情况

  1. #include <cstdio>
  2. #include <cstring>
  3. #include <cstdlib>
  4. #include <vector>
  5. #include <algorithm>
  6. using std::vector;
  7. typedef unsigned int uint;
  8. const int EDGE_SIZE = 524288;
  9. const int POINT_SIZE = 131072;
  10. struct edge_list {
  11. int point, color, tri;
  12. edge_list();
  13. edge_list(int, int);
  14. bool operator < (const edge_list & ) const;
  15. };
  16. struct edge_tuple {
  17. int u, v, color;
  18. void get();
  19. };
  20. int getint();
  21. uint comb_2(int); // equal to C(n, 2)
  22. uint comb_3(int); // equal to C(n, 3)
  23. void add_edge(int, int, int);
  24. void init(int);
  25. bool cmp_color(const edge_list & , const edge_list & );
  26. edge_tuple a[EDGE_SIZE];
  27. vector<edge_list> e[POINT_SIZE];
  28. vector<std::pair<int, int> > common;
  29. int deg[POINT_SIZE];
  30. int tri[POINT_SIZE];
  31. int tri0[POINT_SIZE];
  32. int main() {
  33. freopen("triangle.in", "r", stdin);
  34. freopen("triangle.out", "w", stdout);
  35. uint T, n, m, p;
  36. uint ans;
  37. for (T = getint(); T; T--) {
  38. n = getint(), m = getint(), p = getint();
  39. uint tmp = m * (m - 1) * (m - 2);
  40. init(n);
  41. for (int i = 0; i < p; i++) {
  42. a[i].get();
  43. deg[a[i].u]++;
  44. deg[a[i].v]++;
  45. }
  46. for (int i = 0; i < p; i++)
  47. if (deg[a[i].u] < deg[a[i].v])
  48. add_edge(a[i].u, a[i].v, a[i].color);
  49. else
  50. add_edge(a[i].v, a[i].u, a[i].color);
  51. for (int i = 1; i <= n; i++)
  52. std::sort(e[i].begin(), e[i].end());
  53. ans = comb_3(n) * tmp;
  54. //type3
  55. for (int u = 1; u <= n; u++)
  56. for (int i = 0; i < e[u].size(); i++) {
  57. int v = e[u][i].point;
  58. int j = 0, k = 0;
  59. common.clear();
  60. while (j < e[u].size() && k < e[v].size()) {
  61. for ( ; j < e[u].size() && e[u][j].point < e[v][k].point; j++);
  62. if (j >= e[u].size()) break;
  63. for ( ; k < e[v].size() && e[v][k].point < e[u][j].point; k++);
  64. if (k >= e[v].size()) break;
  65. if (e[u][j].point == e[v][k].point)
  66. common.push_back(std::make_pair(j, k)), j++, k++;
  67. }
  68. for (int j = 0; j < common.size(); j++) {
  69. int w = e[u][common[j].first].point;
  70. int c1, c2, c3;
  71. e[u][i].tri++;
  72. e[u][common[j].first].tri++;
  73. e[v][common[j].second].tri++;
  74. c1 = e[u][i].color;
  75. c2 = e[u][common[j].first].color;
  76. c3 = e[v][common[j].second].color;
  77. tri[u]++, tri[v]++, tri[w]++;
  78. if (c1 != c2 && c2 != c3 && c3 != c1)
  79. ans -= tmp - 1;
  80. else {
  81. ans -= tmp;
  82. if (c1 == c2) tri0[u]++;
  83. if (c1 == c3) tri0[v]++;
  84. if (c2 == c3) tri0[w]++;
  85. }
  86. }
  87. }
  88. //type1
  89. for (int u = 1; u <= n; u++)
  90. for (int i = 0; i < e[u].size(); i++) {
  91. int v = e[u][i].point;
  92. uint cnt = n - deg[u] - deg[v] + e[u][i].tri;
  93. ans -= cnt * (tmp - (m - 1) * (m - 2));
  94. }
  95. //type2
  96. for (int i = 0; i < p; i++)
  97. if (deg[a[i].u] >= deg[a[i].v])
  98. add_edge(a[i].u, a[i].v, a[i].color);
  99. else
  100. add_edge(a[i].v, a[i].u, a[i].color);
  101. for (int i = 1; i <= n; i++) {
  102. std::sort(e[i].begin(), e[i].end(), cmp_color);
  103. uint cnt = comb_2(deg[i]) - tri[i];
  104. ans -= cnt * (tmp - (m - 2));
  105. cnt = 1;
  106. for (int j = 1; j < e[i].size(); j++)
  107. if (e[i][j].color == e[i][j - 1].color)
  108. cnt++;
  109. else {
  110. ans -= comb_2(cnt) * (m - 2);
  111. cnt = 1;
  112. }
  113. ans -= comb_2(cnt) * (m - 2);
  114. ans += tri0[i] * (m - 2);
  115. }
  116. if (m < 3) ans = 0;
  117. printf("%u\n", ans);
  118. }
  119. return 0;
  120. }
  121. edge_list::edge_list(int _point, int _color) {
  122. point = _point;
  123. color = _color;
  124. tri = 0;
  125. }
  126. bool edge_list::operator < (const edge_list & other) const {
  127. return point < other.point;
  128. }
  129. void edge_tuple::get() {
  130. u = getint();
  131. v = getint();
  132. color = getint();
  133. }
  134. int getint() {
  135. int num = 0;
  136. char ch;
  137. do ch = getchar(); while (ch < '0' || ch > '9');
  138. do num = num * 10 + ch - '0', ch = getchar();
  139. while (ch >= '0' && ch <= '9');
  140. return num;
  141. }
  142. uint comb_2(int n) {
  143. uint f = 1;
  144. if (n < 2)
  145. return 0;
  146. if (n & 1)
  147. f = n - 1 >> 1, f *= n;
  148. else
  149. f = n >> 1, f *= n - 1;
  150. return f;
  151. }
  152. uint comb_3(int n) {
  153. uint f = 1, a = n, b = n - 1, c = n - 2;
  154. if (n < 3)
  155. return 0;
  156. if (a % 3 == 0) a /= 3;
  157. else if (b % 3 == 0) b /= 3;
  158. else c /= 3;
  159. if (a & 1) b >>= 1;
  160. else a >>= 1;
  161. f = a * b * c;
  162. return f;
  163. }
  164. void add_edge(int u, int v, int color) {
  165. e[u].push_back(edge_list(v, color));
  166. }
  167. void init(int n) {
  168. for (int i = 1; i <= n; i++)
  169. e[i].clear();
  170. memset(tri, 0, sizeof(tri));
  171. memset(tri0, 0, sizeof(tri0));
  172. memset(deg, 0, sizeof(deg));
  173. }
  174. bool cmp_color(const edge_list & a, const edge_list & b) {
  175. return a.color < b.color;
  176. }

一部分闲扯

是素数:
较小就Lucas

较大就打表

不大不小就递归求解

比如,求

那么可以分成

1 2 3 4 5 6 1 8 9 10 11 12 13 2

转化:
1 2 3 4 5 6 1 1 2 3 4 5 6 2 1 2 3

{1~6} 1 {1~6} 2 {1~6} 3

可以预处理,剩下的就是递归求解子问题


需要掌握的三个调试方法

  1. GDB调试
  2. 输出调试
  3. 肉眼调试

注意肉眼调试并不是傻盯着代码发呆,而是建立在自己血泪史上的调试方法,要对自己哪里容易写错非常敏感

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注