• Codeforces Round #830 (Div. 2)(A~D)


    A. Bestie

    给出一个数组a,通过下面若干次操作使得所有数的gcd为1:选择任意一个数,将它换成gcd(a[i],i),代价是n-i+1,计算将数组变为满足条件所需的最小代价。

    思路:首先我们需要知道,只要数组中有两个数互质就可以满足条件,因为要最小化代价,所以从最后向前修改一定是最优的,如果数组已经满足条件了,那代价为0;若修改最后一个可以满足条件,那就只修改最后一个,代价为1;若只修改倒数第二个就可以满足条件,那代价为2;若前面的操作都不能满足条件,那一定是要修改两个,即倒数两个,代价为3。

    AC Code:

    1. #include
    2. typedef long long ll;
    3. const int N=1e5+5;
    4. int t,n;
    5. int a[N];
    6. int main(){
    7. std::ios::sync_with_stdio(false);
    8. std::cin.tie(0);
    9. std::cout.tie(0);
    10. std::cin>>t;
    11. while(t--){
    12. std::cin>>n;
    13. for(int i=1;i<=n;i++){
    14. std::cin>>a[i];
    15. }
    16. int gcd=std::__gcd(a[1],a[2]);
    17. for(int i=3;i<=n;i++){
    18. gcd=std::__gcd(gcd,a[i]);
    19. }
    20. if(n==1){
    21. if(a[1]==1) std::cout<<0<<'\n';
    22. else std::cout<<1<<'\n';
    23. continue;
    24. }
    25. if(gcd==1){
    26. std::cout<<0<<'\n';
    27. continue;
    28. }
    29. int x=a[n];
    30. a[n]=std::__gcd(a[n],n);
    31. gcd=std::__gcd(a[1],a[2]);
    32. for(int i=3;i<=n;i++){
    33. gcd=std::__gcd(gcd,a[i]);
    34. }
    35. if(gcd==1){
    36. std::cout<<1<<'\n';
    37. continue;
    38. }
    39. a[n]=x;
    40. a[n-1]=std::__gcd(a[n-1],n-1);
    41. gcd=std::__gcd(a[1],a[2]);
    42. for(int i=3;i<=n;i++){
    43. gcd=std::__gcd(gcd,a[i]);
    44. }
    45. if(gcd==1){
    46. std::cout<<2<<'\n';
    47. continue;
    48. }
    49. std::cout<<3<<'\n';
    50. }
    51. return 0;
    52. }

     B. Ugu

    给出一个01串,每次可以选择一个位置,将这个位置以后的所有数全部翻转,即0变为1,1变为0,求最少的操作次数,使得01串变为非递减序列。

    思路:贪心求解,每次遇到与前一个状态不同的都要翻转,要保证0在前。

    AC Code:

    1. #include
    2. typedef long long ll;
    3. const int N=1e5+5;
    4. int t,n;
    5. std::string s;
    6. int main(){
    7. std::ios::sync_with_stdio(false);
    8. std::cin.tie(0);
    9. std::cout.tie(0);
    10. std::cin>>t;
    11. while(t--){
    12. std::cin>>n;
    13. std::cin>>s;
    14. int cnt=0;
    15. int flag=0;
    16. for(int i=1;i
    17. if(s[i]-1]&&!flag){
    18. cnt++;
    19. flag^=1;
    20. }
    21. if(s[i]>s[i-1]&&flag){
    22. cnt++;
    23. flag^=1;
    24. }
    25. }
    26. std::cout<'\n';
    27. }
    28. return 0;
    29. }

    C1. Sheikh (Easy version) 

    给出一个数组a,只有一个询问,给出L,R,在a[L],a[R]之间找一个区间[l,r],使得区间内所有数之和-区间内所有数异或和的值最大且长度最短。

    思路: 分析可知,定义的f(l,r)函数必定是随着区间长度变长而增加,因为数字中无负值,异或和每加入一个数异或,只能小于等于之前的值,所以这个函数是单调递增的,我们可以采用二分解决。考虑对于长度二分,check函数内枚举左端点,根据最长的区间的函数值判断修改左右端点。

    AC Code:

    1. #include
    2. typedef long long ll;
    3. const int N = 2e5 + 5;
    4. int t, pos1, pos2;
    5. ll n, q, ans, L, R;
    6. ll dif[N], xo[N], a[N];
    7. bool check(int mid) {
    8. for (int i = 1; i <= n - mid + 1; i ++) {
    9. ll res = dif[i + mid - 1] - dif[i - 1] - (xo[i + mid - 1] ^ xo[i - 1]);
    10. if (res == ans) {
    11. pos1 = i, pos2 = i + mid - 1;
    12. return true;
    13. }
    14. }
    15. return false;
    16. }
    17. int main () {
    18. std::ios::sync_with_stdio(false);
    19. std::cin.tie(0);
    20. std::cout.tie(0);
    21. std::cin >> t;
    22. while (t --) {
    23. std::cin >> n >> q;
    24. for (int i = 1; i <= n; i ++) {
    25. std::cin >> a[i];
    26. dif[i] = dif[i - 1] + a[i];
    27. xo[i] = xo[i - 1] ^ a[i];
    28. }
    29. pos1 = 0, pos2 = 0;
    30. while (q --) {
    31. std::cin >> L >> R;
    32. ans = dif[R] - dif[L - 1] - (xo[R] ^ xo[L - 1]);
    33. int l = 1, r = n;
    34. while (l < r) {
    35. int mid = l + r >> 1;
    36. if (check(mid))
    37. r = mid;
    38. else
    39. l = mid + 1;
    40. }
    41. if (l == n) pos1 = 1, pos2 = n;
    42. std:: cout << pos1 << ' ' << pos2 << '\n';
    43. }
    44. }
    45. return 0;
    46. }

    C2. Sheikh (Hard Version)

    题意同C1,但是q的范围与n相同,不再是1了。 

    思路:如果还是按照C1的思路,时间复杂度为O(nqlogn),肯定不行,考虑优化。首先我们对于开头结尾的0而言,去掉不影响结果,所以一定要去掉;对于中间的数,我们可以定左端点,二分找右端点,需要处理每个数后面最近的非0数,枚举这些数为左端点;最重要的优化是,因为数据范围可以用64位(大约)二进制数表示,所以最多删去64个数,仅当每个数对于答案中某一位的1有唯一的贡献,例如若答案是111111,则最多有6个数对答案有贡献,即每个数贡献答案中的一位。为什么从左侧删除呢,是因为其实而言是整个数组最多删除64个数(大约),但是这64个数也有可能是左侧的,且因为右端点我们二分查找解决了,所以可以仅考虑左端点。所以n次的枚举变成了仅有64次,时间复杂度O(64*qlogn),可以过。

    AC Code:

    1. #include
    2. typedef long long ll;
    3. const int N = 1e5 + 5;
    4. const int mod = 1e9 + 7;
    5. #define INF 0x3f3f3f3f
    6. int t, n, q, L, R;
    7. ll a[N], sum[N], xo[N];
    8. int next[N];
    9. int main() {
    10. std::ios::sync_with_stdio(false);
    11. std::cin.tie(0);
    12. std::cout.tie(0);
    13. std::cin >> t;
    14. while(t --) {
    15. std::cin >> n >> q;
    16. for(int i = 1; i <= n; i ++) {
    17. std::cin >> a[i];
    18. }
    19. for(int i = 1; i <= n; i ++) {
    20. sum[i] = sum[i - 1] + a[i];
    21. xo[i] = xo[i - 1] ^ a[i];
    22. }
    23. next[n] = INF;
    24. for(int i = n - 1; i >= 1; i --) {
    25. if(a[i + 1] > 0)
    26. next[i] = i + 1;
    27. else
    28. next[i] = next[i + 1];
    29. }
    30. int st, cnt, len, l, r;
    31. while(q --) {
    32. std::cin >> L >> R;
    33. ll ans = sum[R] - sum[L - 1] - (xo[R] ^ xo[L - 1]);
    34. st = L, cnt = 64; len = R - L + 1, l = L, r = R;
    35. while(st <= R) {
    36. if(sum[R] - sum[st - 1] - (xo[R] ^ xo[st - 1]) < ans)
    37. break;
    38. int left = st, right = R, mid;
    39. while(left <= right) {
    40. mid = left + right >> 1;
    41. if(sum[mid] - sum[st - 1] - (xo[mid] ^ xo[st - 1]) == ans)
    42. right = mid - 1;
    43. else
    44. left = mid + 1;
    45. }
    46. if(len > left - st + 1)
    47. len = left - st + 1, l = st, r = left;
    48. if(a[st] > 0) {
    49. if(!cnt) break;
    50. cnt--;
    51. }
    52. st = next[st];
    53. if(st == INF)
    54. break;
    55. }
    56. std::cout << l << ' ' << r << '\n';
    57. }
    58. }
    59. return 0;
    60. }

    D1. Balance (Easy version)

    有一个一开始只有0的集合,每次有两种操作,可以向集合中加入一个数x,也可以询问集合的最小的不在集合中的x的倍数。

    思路:暴力求解即可,但是对于前面出现过的数字,在下一次再开始查找时要从之前的数字开始,这样做一个简单的优化,保证不会TLE。

    AC Code:

    1. #include
    2. typedef long long ll;
    3. const int N=1e5+5;
    4. int q;
    5. int main(){
    6. std::ios::sync_with_stdio(false);
    7. std::cin.tie(0);
    8. std::cout.tie(0);
    9. std::cin>>q;
    10. std::setss;
    11. ss.insert(0);
    12. std::mapmp;
    13. while(q--){
    14. char c;
    15. ll x;
    16. std::cin>>c>>x;
    17. if(c=='+'){
    18. ss.insert(x);
    19. }
    20. else{
    21. ll t=x,tmp=mp[t];
    22. if(tmp&&!ss.count(mp[t])){
    23. std::cout<'\n';
    24. continue;
    25. }
    26. if(tmp!=0) t=mp[t];
    27. while(ss.count(t)){
    28. t+=x;
    29. }
    30. mp[x]=t;
    31. std::cout<'\n';
    32. }
    33. }
    34. return 0;
    35. }

    os:一开始因为集合中没加0WA5了。。。xD

    D2. Balance (Hard version)

    题意与D1相同,只是加入一个删除操作。

    思路:参考hpgg的思路!在C1的基础上再开一个set存储被删除的数,每次加入时如果这个set中有这个数,那就从原来集合中删去,在输出答案时,每次在被删除的set中二分找第一个大于x的数输出,如果找到的数大于mp[x]了,就从mp[x]开始暴力查询结果。

    AC Code:

    1. #include
    2. typedef long long ll;
    3. const int N = 1e5 + 5;
    4. int q;
    5. int main() {
    6. std::ios::sync_with_stdio(false);
    7. std::cin.tie(0);
    8. std::cout.tie(0);
    9. std::cin >> q;
    10. std::set ss, del;
    11. ss.insert(0);
    12. std::map mp;
    13. while(q --) {
    14. char c;
    15. ll x;
    16. std::cin >> c >> x;
    17. if(c == '+') {
    18. if(del.count(x)) del.erase(x);
    19. ss.insert(x);
    20. }
    21. else if(c == '-') {
    22. ss.erase(x);
    23. del.insert(x);
    24. }
    25. else {
    26. ll t = x;
    27. if(!mp[t]) {
    28. while(ss.count(t))
    29. t += x;
    30. mp[x] = t;
    31. std::cout << mp[x] << '\n';
    32. }
    33. else {
    34. auto it = del.lower_bound(x);
    35. bool flag = false;
    36. for(it; it != del.end(); it ++) {
    37. if(*it > mp[x]) break;
    38. if(*it % x == 0) {
    39. std::cout << *it << '\n';
    40. flag = true;
    41. break;
    42. }
    43. }
    44. if(!flag) {
    45. t = mp[x];
    46. while(ss.count(t))
    47. t += x;
    48. std::cout << t << '\n';
    49. mp[x] = t;
    50. }
    51. }
    52. }
    53. }
    54. return 0;
    55. }

    os:部分题解感觉是在胡言乱语,,若有错误请指教

  • 相关阅读:
    AIGC 时代,Amazon DeepRacer 带你驶入机器学习的快车道
    测试用例设计方法六脉神剑——第二剑:招式组合,因果判定出世
    three.js r146动态加载fbx文件,非module模式
    最长公共子序列 递归
    WebGL 的 Hello World
    Java面试题大全(整理版)1000+面试题附答案详解最全面看完稳了
    Lazarus上好用的 Indy TCP client 组件
    MySQL中 JOIN关联查询的原理以及优化手段
    Java全栈工程师:技术与视野的双重拓展
    近似法概率潮流
  • 原文地址:https://blog.csdn.net/m0_62289613/article/details/127521862