Codeforces Global Round 20记录

自闭的构造场。一周不练,该场VP回到解放前qwq。

C-Unequal Array(GOOD)

在这里插入图片描述在这里插入图片描述

思路

  • 最优解的性质:选取的区间一定连续。否则产生2个满足 e q u a t i l i t y equatility equatility条件的一对数
  • 剔除与问题无关的区间。至少要消灭原数组多余的equatity对
    • 方法是作用于第一个满足 a i = a i + 1 a_i = a_{i+1} ai=ai+1的i和最后一个满足 a j = a j + 1 a_j = a_{j+1} aj=aj+1的j组成的区间[i+1,j]。
#include <iostream>
#include <vector>
#include <string>
#include <set>
#include <algorithm>
#include <map>
#include <queue>
#include <chrono>
#include <math.h>
#include <unordered_map>
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;


int main()
{
    int t;
    cin >> t;
    for(int i = 0;i<t;i++)
    {
        int n;
        cin >> n;
        int a[n];
        for(int k = 0;k<n;k++) cin >> a[k];
        int firstid = INT_MAX;
        int lastid = -1;
        for(int k = 0;k<n-1;k++)
        {
            if(a[k] == a[k + 1])
            {
                firstid = min(firstid,k);
                lastid =  max(lastid,k);
            }
        }
        if(lastid == -1 || firstid == lastid) cout << 0 << endl;
        else if(lastid == firstid + 1) cout << 1 << endl;
        else cout << lastid - firstid - 1 << endl;
    }
    system("pause");
    return 0;
}

D题-Cyclic Rotation

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

思路

  • 考察由a生成b的过程,判断匹配:不断将a中与末尾相同的元素移动到末尾。故b中最后一块相等元素块从左往右第一个元素就能和a的最后一个元素匹配。对a的倒数第二个元素,同理。一直到最前(没有考虑倒数第二个及之前的qwq)
  • 利用双指针。从后向前匹配b和a。b中多余的项目计入multiset中。
  • 学习multiset的删除:利用迭代器删除,只删除一个。直接erase,全部删除。
#include <iostream>
#include <vector>
#include <string>
#include <set>
#include <algorithm>
#include <map>
#include <queue>
#include <chrono>
#include <math.h>
#include <unordered_map>
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;


int main()
{
    int t;
    cin >> t;
    for(int i = 0;i<t;i++)
    {
        int n;
        cin >> n;
        int a[n],b[n];
        for(int k = 0;k<n;k++) cin >> a[k];
        for(int k = 0;k<n;k++) cin >> b[k];
        multiset<int> s;
        int p = n - 1,q = n - 1;
        bool flag = true;
        while(p >= 0 && q>=0)
        {
            int curb = b[p];
            while(p - 1 >= 0 && b[p - 1] == curb) 
            {
                s.insert(curb);
                p--;
            }
            if(b[p] == a[q]) {p--;q--;}
            else
            {
                multiset<int>::iterator iter = s.find(a[q]);
                if(iter!=s.end())
                {
                    q--;
                    s.erase(iter);
                }
                else
                {
                    flag = false;
                    break;
                }
            }
        }
        if(!flag)
        {
            cout << "NO" << endl;
        }
        else
        {
            while(q >=0)
            {
                multiset<int>::iterator iter = s.find(a[q]);
                if(iter!=s.end())
                {
                    s.erase(iter);
                }
                else
                {
                    flag = false;
                    break;
                }
                q--;
            }
            if(!flag) cout << "NO" << endl;
            else cout << "YES" << endl; 
        }
    }
    system("pause");
    return 0;
}

E-notepad.exe(交互题)-搜索空间的减少很GOOD在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

思路

  • n种可能的高度。1-n。
    • 如果在每种高度搜索最小width query数量不可接受。
  • 特殊条件入手:每个单词都摆在一行,之间仅有一个空格。且最后无尾随空格。可以采用二分查找搜索出这个最小width,设为S。
  • 关键一步:对给定的高度H,下界是 S − ( H − 1 ) S - (H - 1) S(H1)(空格最多减少H-1个,且每行无尾随空格)(减小搜索空间).因此对每个高度,最优解的搜索空间为 [ S − H + 1 , S ] [S-H + 1,S] [SH+1,S].
    • 最优解取值必须是H的倍数。而在[S-H+1,S]中仅有 ⌊ S / H ⌋ ⋅ H \lfloor S/H \rfloor \cdot H S/HH满足条件。
    • 因此对每个高度H,查询 w i d t h = ⌊ S / H ⌋ width = \lfloor S/H \rfloor width=S/H,如果查询结果等于H,则更新答案。
  • 总结:本题关键在于求出当H=1,摆放所有单词需要的最小width;其他高度的搜索空间不能超过这个最小width。以及这些其他高度最多能使得这个最小width还能减少的面积(每一行末尾至多能少一个空格)。从而减少对于每个高度的搜索空间。
#include <iostream>
#include <vector>
#include <string>
#include <set>
#include <algorithm>
#include <map>
#include <queue>
#include <chrono>
#include <math.h>
#include <unordered_map>
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;


int main()
{
    int n;
    cin >> n;
    int l  =1,r = n * 2000  + 1 + n;
    while(l < r)
    {
        int m = (l + r) >> 1;
        cout << "?" << " " << m << endl;
        cout.flush();
        int h_w; cin >> h_w;
        if(h_w != 1) l = m + 1;
        else r = m;
    }
    int a = l;
    for(int H = 2;H<=n;H++)  //对每个高度搜索最优解
    {
        cout << "?" << " " << l/H << endl;
        cout.flush();
        int h_w; cin >> h_w;
        if(h_w == H) a = min(a,h_w * (l / H)); 
    }
    cout << "!" << " " << a << endl;
    system("pause");
    return 0;
}

F1题- Array Shuffling(GOOD)-复习置换的知识(very good)在这里插入图片描述

在这里插入图片描述

思路(使用Graph表示)

  • a中每个数字表示一个结点,若i位置需要交换到j位置,则从i对应的结点到j对应的结点连一条边
  • 则图是不相交的环构成的集合。设环的数量为 k k k个,每个环表示k-1次交换.因此总共n-k次交换
  • 交换次数上界:保证每个环上不存在相同的元素,否则可以拆成两个环.设数组中出现最多的数的次数为cnt,则sadness最大只可能取到n-cnt
  • 存在使得sadness为n-cnt的构造算法.只需设置n-cnt个组别,然后依次加入数据即可.
#include <iostream>
#include <vector>
#include <string>
#include <set>
#include <algorithm>
#include <map>
#include <queue>
#include <chrono>
#include <math.h>
#include <unordered_map>
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
vector<int> cnt[200001];

int main()
{
    int t;
    cin >> t;
    for(int i = 0;i<t;i++)
    {
        int n;
        cin >> n;
        int max = -1;
        int maxnum = -1;
        int d[n];
        set<int> c;
        for(int k = 0;k<n;k++) {
            int a;cin >> a;cnt[a].push_back(k);d[k]=a;
            if((int)cnt[a].size() > max) 
            {max = cnt[a].size();maxnum = a;}
            c.insert(a);
        }
        vector<int> group[max];
        int cur  =  0;
        for(int k : c)
        {
            while(cnt[k].size() > 0)
            {
                group[cur].push_back(cnt[k][cnt[k].size() - 1]);
                cnt[k].pop_back();
                cur = (cur + 1) % max;
            }   
        }
        //交换数组d
        for(int k = 0;k<max;k++)
        {
            for(int q = 0;q<group[k].size() - 1;q++)
            {
                swap(d[group[k][q]],d[group[k][q+1]]);
            }
        }
        for(int k = 0;k<n-1;k++) cout << d[k] << " ";
        cout << d[n-1]<<endl;
    }
    system("pause");
    return 0;
}

F2-Checker for Array Shuffling V e r y   G O O D Very \ GOOD Very GOOD在这里插入图片描述

思路

#include <iostream>
#include <vector>
#include <string>
#include <set>
#include <algorithm>
#include <map>
#include <queue>
#include <chrono>
#include <math.h>
#include <unordered_map>
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
vector<int> cnt[200001];

bool dfs(bool vis[],int n,bool instack[],int cur)
{
    instack[cur] = true;
    vis[cur] = true;
    for(int u : cnt[cur])
    {
        if(instack[u] == true) return true;
        if(vis[u] == true) continue;
        if(dfs(vis,n,instack,u) == true) return true;
    }
    instack[cur] = false;
    return false;
}




void solve()
{
    int n;
    cin >> n;
    int a[n];
    for(int i = 0;i<n;i++) cin >> a[i]; 
    int max_num = 0;
    int max_cnt = -1;
    for(int i = 0;i<n;i++) 
    {   int b;
        cin >> b;
        cnt[a[i]].push_back(b);
        if((int)cnt[a[i]].size() > max_cnt) {max_num = a[i];max_cnt = cnt[a[i]].size();}  //统计出现次数最多的数字
    }
    bool vis[n+1];
    bool instack[n+1];
    memset(vis,0,(n+1)*sizeof(bool));
    memset(instack,0,(n+1)*sizeof(bool));
    //运行dfs判断是否有回路 
    vis[max_num] = true;
    bool flag = true;
    for(int v:a)
    {
        if(!vis[v])
        {
            if(dfs(vis,n,instack,v))
            {
                flag = false;
                break;
            }
        }
    }
    if(flag) cout << "AC" << endl;else cout<<"WA"<<endl;
    for(int v : a) cnt[v].clear();
}
    

int main()
{
    int t;
    cin >> t;
    while(t--) solve();   
    system("pause");
    return 0;
}

H题-Zigu Zagu

在这里插入图片描述
在这里插入图片描述

思路(直观)

  • A:对0,1对删除。(0,1段相接)
  • B:然后在每段单独删除多余的0,1(直到每段留下1个)
  • A + B = max( ∑ ( 连 续 0 段 中 0 的 个 数 − 1 ) , ∑ ( 连 续 1 段 中 1 的 个 数 − 1 ) \sum (连续0段中0的个数 - 1),\sum (连续1段中1的个数- 1) (001),(111)),求和为对所有的连续0段,1段求和。
  • 最后构成一个尽可能长的0-1交替串,删除这个交替串。
  • 最终操作数:A+B-1
  • 方法:利用前缀和统计;
#include <iostream>
#include <vector>
#include <string>
#include <set>
#include <algorithm>
#include <map>
#include <queue>
#include <chrono>
#include <math.h>
#include <unordered_map>
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

    

int main()
{
    int n,q;
    cin >> n >> q;
    string a;
    cin >> a;  //长度为n的string字符串
    int pre0[n];
    int pre1[n];
    bool mark[n];
    memset(mark,1,n*sizeof(bool));
    int k = 0;
    while(k < n)
    {
        mark[k] = false;
        char cur  =  a[k];
        int pre0_ = k == 0?0:pre0[k-1];
        int pre1_ = k == 0?0:pre1[k-1];
        pre0[k] = pre0_;
        pre1[k] = pre1_;
        k++;
        while(k<n && a[k] == cur) 
        {
            if(cur=='0') {pre0[k] = pre0[k-1] + 1;pre1[k] = pre1[k-1];}
            else {pre1[k]=pre1[k-1]+1;pre0[k] = pre0[k-1];}
            k++;
        }
    }
    for(int i = 0;i<q;i++)
    {
        int l,r;
        scanf("%d %d",&l,&r);
        l--;r--;
        int d;
        if(l-1<=0) d= 0; else d = pre0[l-1];
        int num_0 = pre0[r] - d;
        if(l-1 <=0) d=0; else d = pre1[l-1];
        int num_1 = pre1[r] - d;
        if(mark[l] &&  a[l] == '0') num_0--;
        if(mark[l] &&  a[l] == '1') num_1--;
        printf("%d\n",max(num_0,num_1) + 1);
    }
    system("pause");
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值