• 大三第三周学习笔记


    周一

    Guess

    打了网络赛,还可以,但是不知道哪里可以补题,就写写思路吧

    这道题关键就是从特例推出普遍规律,先讨论简单的情况

    稍微分析一下可以发现,只有2的次幂影响结果,所以设两数为pt 2pt

    t为奇数,p为2的次幂,t不影响结果所以设t=1

    如果是1 2   显然2的人赢

    如果是4 2   2的人说自己i dont know那么到4的时候就知道自己不是1,4的人赢

    如果是4 8   4的人说自己i dont know  8的人就知道自己不是2,8的人赢

    总结一下,数字大的人赢,这里有点递推的感觉。

    Mutiple Set

    这题不难,不要有畏难情绪,自己给自己设限

    稍微推一下,设l为l/x上取整,r为r/x上取整,那么K=2^(r-l-1)x(l+r)(r-l+1)

    这个式子的变量是x,l和r是题目给的。可以发现x是K的因子,对于这个式子,只要确定了x,那么可以计算l和r,那么就可以验证是否等于K

    因子需要求K的所有因子,K是10^14,直接根号K枚举的话加上多组数据会T。因此考虑对K进行质因数分解,通过枚举指数来枚举因子。

    这里有两种方法,一种是预处理10^7的质数,用这些质数去质因数分解,一种是大数分解。

    poj 1811(素数测试和大数质因数分解)

    主要是熟悉板子

    Miller_Rabin素数测试可以迅速测试一个大数(long long级别)是否为质数

    pollard_rho算法可以对一个long long级别的数质因数分解。

    注意板子中质因数分解的结果是无序重复的素数,如24就是2 3 2 2

    1. #include <cstdio>
    2. #include <ctime>
    3. #include <cstdlib>
    4. #include <algorithm>
    5. #define rep(i, a, b) for(int i = (a); i < (b); i++)
    6. #define _for(i, a, b) for(int i = (a); i <= (b); i++)
    7. using namespace std;
    8. typedef long long ll;
    9. const int S = 8;
    10. ll mult_mod(ll a, ll b, ll c)
    11. {
    12. a %= c;
    13. b %= c;
    14. ll ret = 0, tmp = a;
    15. while(b)
    16. {
    17. if(b & 1)
    18. {
    19. ret += tmp;
    20. if(ret > c) ret -= c;
    21. }
    22. tmp <<= 1;
    23. if(tmp > c) tmp -= c;
    24. b >>= 1;
    25. }
    26. return ret;
    27. }
    28. ll pow_mod(ll a, ll n, ll mod)
    29. {
    30. ll ret = 1;
    31. ll temp = a % mod;
    32. while(n)
    33. {
    34. if(n & 1) ret = mult_mod(ret, temp, mod);
    35. temp = mult_mod(temp, temp, mod);
    36. n >>= 1;
    37. }
    38. return ret;
    39. }
    40. bool check(ll a, ll n, ll x, ll t)
    41. {
    42. ll ret = pow_mod(a, x, n);
    43. ll last = ret;
    44. _for(i, 1, t)
    45. {
    46. ret = mult_mod(ret, ret, n);
    47. if(ret == 1 && last != 1 && last != n - 1) return true;
    48. last = ret;
    49. }
    50. if(ret != 1) return true;
    51. return false;
    52. }
    53. bool Miller_Rabin(ll n)
    54. {
    55. if(n < 2) return false;
    56. if(n == 2) return true;
    57. if((n & 1) == 0) return false;
    58. ll x = n - 1, t = 0;
    59. while((x & 1) == 0) x >>= 1, t++;
    60. srand(time(0));
    61. rep(i, 0, S)
    62. {
    63. ll a = rand() % (n - 1) + 1;
    64. if(check(a, n, x, t)) return false;
    65. }
    66. return true;
    67. }
    68. ll factor[100]; //储存质因数分解结果 有重复 无序
    69. int tol; //标号0~tol-1
    70. ll gcd(ll a, ll b)
    71. {
    72. ll t;
    73. while(b)
    74. {
    75. t = a;
    76. a = b;
    77. b = t % b;
    78. }
    79. if(a >= 0) return a;
    80. return -a;
    81. }
    82. ll pollard_rho(ll x, ll c)
    83. {
    84. ll i = 1, k = 2;
    85. srand(time(0));
    86. ll x0 = rand() % (x - 1) + 1;
    87. ll y = x0;
    88. while(1)
    89. {
    90. i++;
    91. x0 = (mult_mod(x0, x0, x) + c) % x;
    92. ll d = gcd(y - x0, x);
    93. if(d != 1 && d != x) return d;
    94. if(y == x0) return x;
    95. if(i == k) y = x0, k += k;
    96. }
    97. }
    98. void findfac(ll n, ll k)
    99. {
    100. if(n == 1) return;
    101. if(Miller_Rabin(n))
    102. {
    103. factor[tol++] = n;
    104. return;
    105. }
    106. ll p = n;
    107. int c = k;
    108. while(p >= n) p = pollard_rho(p, c--);
    109. findfac(p, k);
    110. findfac(n / p, k);
    111. }
    112. int main()
    113. {
    114. int T; scanf("%d", &T);
    115. while(T--)
    116. {
    117. ll n;
    118. scanf("%lld", &n);
    119. if(Miller_Rabin(n)) puts("Prime");
    120. else
    121. {
    122. tol = 0;
    123. findfac(n, 107);
    124. ll ans = factor[0];
    125. rep(i, 1, tol)
    126. ans = min(ans, factor[i]);
    127. printf("%lld\n", ans);
    128. }
    129. }
    130. return 0;
    131. }

    Tree Partition(二分+贪心+树上dp)

    二分答案读完题就知道了,关键是确定了最大值后,怎么操作使得删掉的边最少,这是这道题的关键。

    这里要用到树形dp,dp[u]表示以u为根的子树符合条件的情况下,u所在的连通块的权值和是多少。

    那么每次u自己和全部dp[v]算上,如果超过了最大值,那么就考虑删边。

    为了使得删的边数最少,一次删边要去掉尽可能多的权值,所以删去最大的dp[v],还是超就删第二大的dp[v]以此类推。

    这样子贪心下来就可以求出删了多少边了

    1. #include <bits/stdc++.h>
    2. #define rep(i, a, b) for(int i = (a); i < (b); i++)
    3. #define _for(i, a, b) for(int i = (a); i <= (b); i++)
    4. using namespace std;
    5. typedef long long ll;
    6. const int N = 2e5 + 10;
    7. int a[N], n, k, mx, cnt;
    8. vector<int> g[N];
    9. ll key, dp[N];
    10. void dfs(int u, int fa)
    11. {
    12. dp[u] = a[u];
    13. ll sum = a[u];
    14. vector<ll> ve;
    15. for(int v: g[u])
    16. {
    17. if(v == fa) continue;
    18. dfs(v, u);
    19. ve.push_back(dp[v]);
    20. sum += dp[v];
    21. }
    22. if(!ve.size()) return;
    23. sort(ve.begin(), ve.end(), greater<ll>());
    24. rep(i, 0, ve.size())
    25. {
    26. if(sum > key) sum -= ve[i], cnt++;
    27. else break;
    28. }
    29. dp[u] = sum;
    30. }
    31. bool check(ll x)
    32. {
    33. cnt = 0;
    34. key = x;
    35. dfs(1, 0);
    36. return cnt <= k;
    37. }
    38. int main()
    39. {
    40. int T, kase = 0;
    41. scanf("%d", &T);
    42. while(T--)
    43. {
    44. mx = 0;
    45. scanf("%d%d", &n, &k); k--;
    46. _for(i, 1, n) g[i].clear();
    47. _for(i, 1, n - 1)
    48. {
    49. int u, v;
    50. scanf("%d%d", &u, &v);
    51. g[u].push_back(v);
    52. g[v].push_back(u);
    53. }
    54. _for(i, 1, n) scanf("%d", &a[i]), mx = max(mx, a[i]);
    55. ll l = mx - 1, r = 1e15;
    56. while(l + 1 < r)
    57. {
    58. ll m = l + r >> 1;
    59. if(check(m)) r = m;
    60. else l = m;
    61. }
    62. printf("Case #%d: %lld\n", ++kase, r);
    63. }
    64. return 0;
    65. }

    C. Digital Logarithm(思维)

    很容易发现操作几次就变1了,考虑怎么让它最小。

    注意到每次操作都是变小的,所以如果一个数是最大的,大于另一个序列的最大值,那么它一定要变小,这个是必须要操作的。因此就根据这一点,比较两者的最大值,相等就不用操作,否则就变小,用优先队列实现。

    cf题解的stl用的很秀啊,代码很短。

    1. #include <bits/stdc++.h>
    2. #define rep(i, a, b) for(int i = (a); i < (b); i++)
    3. #define _for(i, a, b) for(int i = (a); i <= (b); i++)
    4. using namespace std;
    5. int main()
    6. {
    7. int T; scanf("%d", &T);
    8. while(T--)
    9. {
    10. int n; scanf("%d", &n);
    11. vector<int> a(n), b(n);
    12. rep(i, 0, n) scanf("%d", &a[i]);
    13. rep(i, 0, n) scanf("%d", &b[i]);
    14. int ans = 0;
    15. priority_queue<int> qa(a.begin(), a.end());
    16. priority_queue<int> qb(b.begin(), b.end());
    17. while(!qa.empty())
    18. {
    19. if(qa.top() == qb.top())
    20. {
    21. qa.pop(); qb.pop();
    22. continue;
    23. }
    24. else if(qa.top() > qb.top())
    25. {
    26. qa.push(to_string(qa.top()).size());
    27. qa.pop(); ans++;
    28. }
    29. else
    30. {
    31. qb.push(to_string(qb.top()).size());
    32. qb.pop(); ans++;
    33. }
    34. }
    35. printf("%d\n", ans);
    36. }
    37. return 0;
    38. }

    但我比赛时是另一个思路,大致是先去重,然后把二位数以上的操作,再去重,然后把非1的操作。但是我写去重的时候用了很多stl,常数很大,赛时AC,赛后T了……

    其实就排个序然后用两个指针即可,不用弄那么复杂。改成这么写后AC了

    1. #include <bits/stdc++.h>
    2. #define rep(i, a, b) for(int i = (a); i < (b); i++)
    3. #define _for(i, a, b) for(int i = (a); i <= (b); i++)
    4. using namespace std;
    5. const int N = 2e5 + 10;
    6. int a[N], b[N], visa[N], visb[N], n;
    7. vector<int> va, vb;
    8. int get(int x)
    9. {
    10. int res = 0;
    11. while(x)
    12. {
    13. x /= 10;
    14. res++;
    15. }
    16. return res;
    17. }
    18. void deal(int a[], int b[], int n)
    19. {
    20. _for(i, 1, n) visa[i] = visb[i] = 0;
    21. sort(a + 1, a + n + 1);
    22. sort(b + 1, b + n + 1);
    23. int pa = 1, pb = 1;
    24. while(pa <= n && pb <= n)
    25. {
    26. if(a[pa] == b[pb])
    27. {
    28. visa[pa] = visb[pb] = 1;
    29. pa++; pb++;
    30. }
    31. else if(a[pa] > b[pb]) pb++;
    32. else pa++;
    33. }
    34. va.clear();
    35. vb.clear();
    36. _for(i, 1, n)
    37. if(!visa[i])
    38. va.push_back(a[i]);
    39. _for(i, 1, n)
    40. if(!visb[i])
    41. vb.push_back(b[i]);
    42. }
    43. int main()
    44. {
    45. int T; scanf("%d", &T);
    46. while(T--)
    47. {
    48. scanf("%d", &n);
    49. _for(i, 1, n) scanf("%d", &a[i]);
    50. _for(i, 1, n) scanf("%d", &b[i]);
    51. deal(a, b, n);
    52. int ans = 0;
    53. rep(i, 0, va.size())
    54. if(va[i] >= 10)
    55. {
    56. va[i] = get(va[i]);
    57. ans++;
    58. }
    59. rep(i, 0, vb.size())
    60. if(vb[i] >= 10)
    61. {
    62. vb[i] = get(vb[i]);
    63. ans++;
    64. }
    65. int cnt = 0;
    66. for(int x: va) a[++cnt] = x;
    67. cnt = 0;
    68. for(int x: vb) b[++cnt] = x;
    69. deal(a, b, cnt);
    70. rep(i, 0, va.size())
    71. if(va[i] > 1)
    72. ans++;
    73. rep(i, 0, vb.size())
    74. if(vb[i] > 1)
    75. ans++;
    76. printf("%d\n", ans);
    77. }
    78. return 0;
    79. }

    周二

    D. Letter Picking(dp)

    之前做过一道优点类似的,也是赢平局输,用dp做的,这题也类似。

    考试时用dp过的,但是写的比较复杂,但大体思路和题解是一致的

    我是在状态转移分类讨论的,而题解是直接取min max的,写起来很方便

    令1为Alice,0为Draw,-1为Bob

    那么Alice的目标是使值变大,Bob是使值变小,所以就转化成了博弈里面的min-max搜索。

    1. #include <bits/stdc++.h>
    2. #define rep(i, a, b) for(int i = (a); i < (b); i++)
    3. #define _for(i, a, b) for(int i = (a); i <= (b); i++)
    4. using namespace std;
    5. const int N = 2000 + 10;
    6. int dp[N][N], n;
    7. char s[N];
    8. int check(char a, char b)
    9. {
    10. return a > b ? 1 : (a == b ? 0 : -1);
    11. }
    12. int main()
    13. {
    14. int T; scanf("%d", &T);
    15. while(T--)
    16. {
    17. scanf("%s", s + 1);
    18. n = strlen(s + 1);
    19. _for(i, 1, n - 1)
    20. {
    21. if(s[i] == s[i + 1]) dp[i][i + 1] = 0;
    22. else dp[i][i + 1] = 1;
    23. }
    24. for(int len = 4; len <= n; len += 2)
    25. _for(l, 1, n)
    26. {
    27. int r = l + len - 1;
    28. if(r > n) break;
    29. int cl = 1;
    30. if(dp[l + 1][r - 1] != 0) cl = min(cl, dp[l + 1][r - 1]);
    31. else cl = min(cl, check(s[l], s[r]));
    32. if(dp[l + 2][r] != 0) cl = min(cl, dp[l + 2][r]);
    33. else cl = min(cl, check(s[l], s[l + 1]));
    34. int cr = 1;
    35. if(dp[l + 1][r - 1] != 0) cl = min(cl, dp[l + 1][r - 1]);
    36. else cr = min(cr, check(s[r], s[l]));
    37. if(dp[l][r - 2] != 0) cl = min(cl, dp[l][r - 2]);
    38. else cr = min(cr, check(s[r], s[r - 1]));
    39. dp[l][r] = max(cl, cr);
    40. }
    41. if(dp[1][n] == 1) puts("Alice");
    42. else if(dp[1][n] == -1) puts("Bob");
    43. else puts("Draw");
    44. }
    45. return 0;
    46. }

    还有一种O(n)的做法,比赛时我就猜测Bob不可能赢,我尝试构造了Bob赢得情况没构造出来。

    如果有了这个结论,那么其实只要判断什么时候平局即可,不是平局就是Alice赢

    如果是平局,那么就是两边拿到字符串是一样的,考虑什么时候会一样。

    因为每次拿都是两侧的,或者一侧两个,那么需要它们是一样的。

    两侧一样的就是对称的,一侧两个就是aabbcc这样

    那么都存在呢,可以发现可以外面是对称的,里面是aabbcc的,那么判断是否是这样的串就可以了

    1. #include <bits/stdc++.h>
    2. #define rep(i, a, b) for(int i = (a); i < (b); i++)
    3. #define _for(i, a, b) for(int i = (a); i <= (b); i++)
    4. using namespace std;
    5. const int N = 2000 + 10;
    6. char s[N];
    7. int n;
    8. int main()
    9. {
    10. int T; scanf("%d", &T);
    11. while(T--)
    12. {
    13. scanf("%s", s + 1);
    14. n = strlen(s + 1);
    15. int l = 1, r = n;
    16. while(l < r && s[l] == s[r]) l++, r--;
    17. int flag = 1;
    18. for(int i = l; i <= r; i += 2)
    19. if(s[i] != s[i + 1])
    20. {
    21. flag = 0;
    22. break;
    23. }
    24. puts(flag ? "Draw" : "Alice");
    25. }
    26. return 0;
    27. }

    B. Appleman and Tree(树形dp)

    这种切割的,就设在以u为根的子树中,包含u的联通块即可

    还要分当前联通块是否有一个黑色,然后分类讨论即可。

    注意儿子所在联通块无黑色时不能切断

    1. #include <bits/stdc++.h>
    2. #define rep(i, a, b) for(int i = (a); i < (b); i++)
    3. #define _for(i, a, b) for(int i = (a); i <= (b); i++)
    4. using namespace std;
    5. typedef long long ll;
    6. const int N = 1e5 + 10;
    7. const int mod = 1e9 + 7;
    8. vector<int> g[N];
    9. ll dp[N][2];
    10. int c[N], n;
    11. ll binpow(ll a, ll b)
    12. {
    13. ll res = 1;
    14. for(; b; b >>= 1)
    15. {
    16. if(b & 1) res = res * a % mod;
    17. a = a * a % mod;
    18. }
    19. return res;
    20. }
    21. ll inv(ll x) { return binpow(x, mod - 2); }
    22. void dfs(int u, int fa)
    23. {
    24. ll res = 1;
    25. if(c[u] == 1)
    26. {
    27. for(int v: g[u])
    28. {
    29. if(v == fa) continue;
    30. dfs(v, u);
    31. if(c[v]) res = res * dp[v][1] % mod;
    32. else res = res * (dp[v][1] + dp[v][0]) % mod;
    33. }
    34. dp[u][1] = res;
    35. }
    36. else
    37. {
    38. for(int v: g[u])
    39. {
    40. if(v == fa) continue;
    41. dfs(v, u);
    42. if(c[v]) res = res * dp[v][1] % mod;
    43. else res = res * (dp[v][1] + dp[v][0]) % mod;
    44. }
    45. dp[u][0] = res;
    46. ll sum = 0;
    47. for(int v: g[u])
    48. {
    49. if(v == fa) continue;
    50. if(c[v]) sum = (sum + res) % mod;
    51. else sum = (sum + res * inv(dp[v][1] + dp[v][0]) % mod * dp[v][1] % mod) % mod;
    52. }
    53. dp[u][1] = sum;
    54. }
    55. }
    56. int main()
    57. {
    58. scanf("%d", &n);
    59. _for(i, 2, n)
    60. {
    61. int x; scanf("%d", &x); x++;
    62. g[x].push_back(i);
    63. g[i].push_back(x);
    64. }
    65. _for(i, 1, n) scanf("%d", &c[i]);
    66. dfs(1, 0);
    67. printf("%lld\n", dp[1][1]);
    68. return 0;
    69. }

    周三

    D. Toss a Coin to Your Graph...(删点+图上最长路径)

    从提问的方式可以看出是二分答案

    二分一个答案,然后先删点。删点的实现可以用一个数组标记,后面遍历点的时候都要判断这是没有被删去的点。注意新图的点数要重新计算,不是n

    得到一个新图后,这时候点权没有用了,只需要判断是否存在长度大于等于k的路径

    如果有环则一定存在,否则用拓扑排序加dp求最长度。判环用拓扑排序。

    注意有-1的情况,二分的边界要注意一下

    1. #include <bits/stdc++.h>
    2. #define rep(i, a, b) for(int i = (a); i < (b); i++)
    3. #define _for(i, a, b) for(int i = (a); i <= (b); i++)
    4. using namespace std;
    5. typedef long long ll;
    6. const int N = 2e5 + 10;
    7. vector<int> g[N];
    8. int a[N], d[N], dp[N], ban[N], n, m;
    9. ll k;
    10. bool check(int key)
    11. {
    12. int cnt = 0;
    13. _for(i, 1, n)
    14. {
    15. if(a[i] <= key) ban[i] = 0, cnt++, dp[i] = 1;
    16. else ban[i] = 1;
    17. d[i] = 0;
    18. }
    19. _for(u, 1, n)
    20. for(int v: g[u])
    21. if(!ban[u] && !ban[v])
    22. d[v]++;
    23. queue<int> q;
    24. vector<int> topo;
    25. _for(i, 1, n)
    26. if(!ban[i] && !d[i])
    27. q.push(i);
    28. while(!q.empty())
    29. {
    30. int u = q.front(); q.pop();
    31. topo.push_back(u);
    32. for(int v: g[u])
    33. if(!ban[v] && --d[v] == 0)
    34. q.push(v);
    35. }
    36. if(topo.size() < cnt) return true;
    37. int res = 0;
    38. for(int u: topo)
    39. for(int v: g[u])
    40. if(!ban[v])
    41. dp[v] = max(dp[v], dp[u] + 1);
    42. for(int u: topo) res = max(res, dp[u]);
    43. return res >= k;
    44. }
    45. int main()
    46. {
    47. scanf("%d%d%lld", &n, &m, &k);
    48. _for(i, 1, n) scanf("%d", &a[i]);
    49. while(m--)
    50. {
    51. int u, v;
    52. scanf("%d%d", &u, &v);
    53. g[u].push_back(v);
    54. }
    55. int l = 0, r = 1e9 + 1;
    56. while(l + 1 < r)
    57. {
    58. int m = l + r >> 1;
    59. if(check(m)) r = m;
    60. else l = m;
    61. }
    62. printf("%d\n", r == 1e9 + 1 ? -1 : r);
    63. return 0;
    64. }

    C. Where is the Pizza?(思维)

    先考虑简单的情况,c全是0

    可以发现确定了一个数,就有一堆数连着被确定,形成一个环。

    于是ai和bi连边,就会形成很多个环。

    环中的一个点确定了就都确定了,可以确定使用a或b

    因此每一个大于1的环就有两种选择

    对于c已经有数,就是已经确定了,不对答案有贡献。

    1. #include <bits/stdc++.h>
    2. #define rep(i, a, b) for(int i = (a); i < (b); i++)
    3. #define _for(i, a, b) for(int i = (a); i <= (b); i++)
    4. using namespace std;
    5. typedef long long ll;
    6. const int mod = 1e9 + 7;
    7. const int N = 1e5 + 10;
    8. int f[N], a[N], b[N], ban[N], siz[N], n;
    9. ll binpow(ll a, ll b)
    10. {
    11. ll res = 1;
    12. for(; b; b >>= 1)
    13. {
    14. if(b & 1) res = res * a % mod;
    15. a = a * a % mod;
    16. }
    17. return res;
    18. }
    19. int find(int x) { return f[x] == x ? x : f[x] = find(f[x]); }
    20. void Union(int x, int y)
    21. {
    22. int fx = find(x), fy = find(y);
    23. if(fx == fy) return;
    24. siz[fx] += siz[fy];
    25. f[fy] = fx;
    26. }
    27. int main()
    28. {
    29. int T; scanf("%d", &T);
    30. while(T--)
    31. {
    32. scanf("%d", &n);
    33. _for(i, 1, n) f[i] = i, ban[i] = 0, siz[i] = 1;
    34. _for(i, 1, n) scanf("%d", &a[i]);
    35. _for(i, 1, n) scanf("%d", &b[i]);
    36. _for(i, 1, n) Union(a[i], b[i]);
    37. _for(i, 1, n)
    38. {
    39. int x; scanf("%d", &x);
    40. ban[find(x)] = 1;
    41. }
    42. int cnt = 0;
    43. _for(i, 1, n)
    44. if(f[i] == i && !ban[i] && siz[i] > 1)
    45. cnt++;
    46. printf("%lld\n", binpow(2, cnt));
    47. }
    48. return 0;
    49. }

    C. Tokitsukaze and Two Colorful Tapes(并查集找环+贪心)

    这题的关键就是像上一题一样,ai向bi连边可以形成环。如果没有做过上一题,很难想到

    有环之后考虑怎么分配数字。一个直觉的想法是一大一小分配,这样让差最大,如何证明呢?

    当存在a1 > a2 > a3时,对答案的贡献是等价于a1 > a3的,也就是中间的a2并没有用。那么对于任意一种分配,删去这种没有用的点后,一定是一大一小交替。而且可以发现,一大一小交替的话,个数一定是偶数。

    这时大的绝对值取正,小的绝对值取负,所以我们让大的尽可能大,小的尽可能小。

    因此,如果环是偶数,那么就大的位置放尽可能大的,小的放尽可能小的。

    对于环是奇数,有一个位置是没有用的,所以要填一个最差的,也就是中间的值,把极值留给其他地方。

    最后注意开long long

    1. #include <bits/stdc++.h>
    2. #define rep(i, a, b) for(int i = (a); i < (b); i++)
    3. #define _for(i, a, b) for(int i = (a); i <= (b); i++)
    4. using namespace std;
    5. typedef long long ll;
    6. const int N = 1e5 + 10;
    7. int f[N], a[N], b[N], v[N], n, l, r, pos;
    8. vector<int> k[N], num;
    9. int find(int x) { return f[x] == x ? x : f[x] = find(f[x]); }
    10. void Union(int x, int y) { f[find(x)] = find(y); }
    11. ll cal(int x)
    12. {
    13. if(x <= 1) return 0;
    14. if(x % 2 == 1) x--;
    15. rep(i, 0, x) v[i] = num[pos++];
    16. ll res = 0;
    17. rep(i, 0, x)
    18. {
    19. int j = (i + 1) % x;
    20. res += abs(v[j] - v[i]);
    21. }
    22. return res;
    23. }
    24. int main()
    25. {
    26. int T; scanf("%d", &T);
    27. while(T--)
    28. {
    29. scanf("%d", &n);
    30. _for(i, 1, n) f[i] = i, k[i].clear();
    31. _for(i, 1, n) scanf("%d", &a[i]);
    32. _for(i, 1, n) scanf("%d", &b[i]);
    33. _for(i, 1, n) Union(a[i], b[i]);
    34. _for(i, 1, n) k[find(i)].push_back(i);
    35. ll ans = 0;
    36. l = 1, r = n;
    37. num.clear(); pos = 0;
    38. while(l <= r)
    39. {
    40. num.push_back(l); l++;
    41. num.push_back(r); r--;
    42. }
    43. _for(i, 1, n) ans += cal(k[i].size());
    44. printf("%lld\n", ans);
    45. }
    46. return 0;
    47. }

    周四

    D. K-good(数学)

    首先注意一波推导,注意余数取k而不是取0,因为数字要是正数,同时两边乘以2去掉分数

    可以得到2n = k(k + 1 + 2t)

    我自己做的时候推到这里就不知道怎么分析了

    首先注意k和k+1+2t,一个是偶数一个奇数

    也就是说2n可以分解为一个偶数和一个奇数相乘。

    因为奇数部分没有2的因子

    所以就直接把2n一直除以2来分解

    然后就成了一个奇数和偶数。但是注意奇数部分不能为1,因为k和k+1+2t不可能为1

    分解完之后,哪一个是k呢,注意到k+1+2t更大,所以k就取奇数和偶数更小的那个即可

    1. #include <bits/stdc++.h>
    2. #define rep(i, a, b) for(int i = (a); i < (b); i++)
    3. #define _for(i, a, b) for(int i = (a); i <= (b); i++)
    4. using namespace std;
    5. typedef long long ll;
    6. int main()
    7. {
    8. int T; scanf("%d", &T);
    9. while(T--)
    10. {
    11. ll n;
    12. scanf("%lld", &n);
    13. n *= 2;
    14. ll t = 1;
    15. while(n % 2 == 0)
    16. {
    17. n /= 2;
    18. t *= 2;
    19. }
    20. if(n == 1) puts("-1");
    21. else printf("%lld\n", min(t, n));
    22. }
    23. return 0;
    24. }

    D. Not Adding(思维)

    首先可以发现,新出现的数肯定是当前数子集的gcd

    然后每个数都是不同的,所以当前序列没有的数很少,那么就去枚举这些输数,然后枚举它的倍数,然后将它的倍数取gcd,如果是它那么就答案加一。

    1. #include <bits/stdc++.h>
    2. #define rep(i, a, b) for(int i = (a); i < (b); i++)
    3. #define _for(i, a, b) for(int i = (a); i <= (b); i++)
    4. using namespace std;
    5. unordered_map<int, bool> vis;
    6. int n, mx;
    7. int gcd(int a, int b) { return !b ? a : gcd(b, a % b); }
    8. int main()
    9. {
    10. scanf("%d", &n);
    11. _for(i, 1, n)
    12. {
    13. int x; scanf("%d", &x);
    14. vis[x] = 1;
    15. mx = max(mx, x);
    16. }
    17. int ans = 0;
    18. _for(i, 1, mx)
    19. if(!vis[i])
    20. {
    21. int cur = 0;
    22. for(int j = i; j <= mx; j += i)
    23. if(vis[j])
    24. cur = gcd(cur, j);
    25. if(cur == i) ans++;
    26. }
    27. printf("%d\n", ans);
    28. return 0;
    29. }

    D2. Half of Same(思维)

    一开始的想法是枚举当前数的因数,然后用这个因数看是多少个数的公因数

    一次的复杂度是max(根号ai,因数个数 * n)  我一开始算成根号ai*n了,然后觉得会T

    1. #include <bits/stdc++.h>
    2. #define rep(i, a, b) for(int i = (a); i < (b); i++)
    3. #define _for(i, a, b) for(int i = (a); i <= (b); i++)
    4. using namespace std;
    5. const int N = 50;
    6. int a[N], n;
    7. unordered_map<int, int> mp;
    8. int main()
    9. {
    10. int T; scanf("%d", &T);
    11. while(T--)
    12. {
    13. mp.clear();
    14. scanf("%d", &n);
    15. int flag = 0;
    16. _for(i, 1, n)
    17. {
    18. scanf("%d", &a[i]);
    19. if(++mp[a[i]] >= n / 2) flag = 1;
    20. }
    21. if(flag)
    22. {
    23. puts("-1");
    24. continue;
    25. }
    26. int ans = 1;
    27. _for(id, 1, n)
    28. {
    29. vector<int> ve;
    30. _for(j, 1, n)
    31. if(a[j] - a[id] >= 0)
    32. ve.push_back(a[j] - a[id]);
    33. if(ve.size() < n / 2) continue;
    34. rep(i, 0, ve.size())
    35. {
    36. for(int j = 1; j * j <= ve[i]; j++)
    37. if(ve[i] % j == 0)
    38. {
    39. int cnt = 0;
    40. rep(k, 0, ve.size())
    41. if(ve[k] % j == 0)
    42. cnt++;
    43. if(cnt >= n / 2) ans = max(ans, j);
    44. cnt = 0;
    45. rep(k, 0, ve.size())
    46. if(ve[k] % (ve[i] / j) == 0)
    47. cnt++;
    48. if(cnt >= n / 2) ans = max(ans, ve[i] / j);
    49. }
    50. }
    51. }
    52. printf("%d\n", ans);
    53. }
    54. return 0;
    55. }

    题解的方法优美一些,把所有因子扔到map里面统计一下有无超过一半,注意相同的情况。

    1. #include <bits/stdc++.h>
    2. #define rep(i, a, b) for(int i = (a); i < (b); i++)
    3. #define _for(i, a, b) for(int i = (a); i <= (b); i++)
    4. using namespace std;
    5. const int N = 50;
    6. int a[N], n;
    7. unordered_map<int, int> mp, vis;
    8. int main()
    9. {
    10. int T; scanf("%d", &T);
    11. while(T--)
    12. {
    13. mp.clear();
    14. scanf("%d", &n);
    15. int flag = 0;
    16. _for(i, 1, n)
    17. {
    18. scanf("%d", &a[i]);
    19. if(++mp[a[i]] >= n / 2) flag = 1;
    20. }
    21. if(flag)
    22. {
    23. puts("-1");
    24. continue;
    25. }
    26. int ans = 1;
    27. _for(id, 1, n)
    28. {
    29. int same = 0;
    30. vector<int> ve;
    31. _for(j, 1, n)
    32. {
    33. if(a[j] - a[id] == 0) same++;
    34. else if(a[j] - a[id] > 0) ve.push_back(a[j] - a[id]);
    35. }
    36. vis.clear();
    37. rep(i, 0, ve.size())
    38. {
    39. for(int j = 1; j * j <= ve[i]; j++)
    40. if(ve[i] % j == 0)
    41. {
    42. vis[j]++;
    43. if(j * j != ve[i]) vis[ve[i] / j]++;
    44. }
    45. }
    46. for(auto x : vis)
    47. if(x.second + same >= n / 2)
    48. ans = max(ans, x.first);
    49. }
    50. printf("%d\n", ans);
    51. }
    52. return 0;
    53. }

  • 相关阅读:
    navicat16 破解(注意破解时候要断网)
    javascript实现常用数组方法重写
    【无标题】mysql 普通用户连接报错: MySql server has gone away
    向量检索之一:Faiss 在工业界的应用和常见问题解决
    使用es实现轻量级分布式锁
    pacemaker+corosync 搭建一主两从PG集群
    集美大学第九届程序设计竞赛 L.序列 逆序对
    “手印”惠及你我,共赴绿色降碳之路
    Java SE 16 新增特性
    【图像融合】基于matlab粒子群优化自适应多光谱图像融合【含Matlab源码 004期】
  • 原文地址:https://blog.csdn.net/qq_34416123/article/details/126813911