头像

名为溶菌酶的溶酶菌




在线 


最近来访(46)
用户头像
y0shino
用户头像
煜.
用户头像
啼莺修竹
用户头像
k_415
用户头像
五柳先生
用户头像
moreexcellent
用户头像
Everglow_1
用户头像
dongrq
用户头像
企鹅聊天图书馆摇头晃
用户头像
羊羊向前冲
用户头像
anonymous233
用户头像
Susanna
用户头像
姜子
用户头像
不高兴的兽奶
用户头像
huangbq
用户头像
Flame_scion
用户头像
爱玩CF的帅小伙
用户头像
Love_Yourself
用户头像
tangguochao
用户头像
にしかた


problem A. Twin Permutations
题目大意:给定一个全排列数组,要求输出另一个全排列数组,使得a1 + b1 <= a2 + b2 <= a3 + b3 <= … <= an + bn
思路:直接让所有数相等即可
数据范围:1 ≤ n ≤ 100 1 ≤ ai ≤ n

#include <iostream>

using namespace std;

int main()
{
    int T; cin >> T;
    while (T --)
    {
        int n; cin >> n;
        for (int i = 0; i < n; i ++)
        {
            int x; cin >> x;
            cout << n - x + 1 << " ";
        }
        cout << endl;
    }
    return 0;
}

problem B. Array merging
题目大意:给定两个长度为n的数组,将它们合并成另一个长度为2n的数组c,每次合并的时候可以从任意数组的开头拿过来一个元素,然后将该元素从原数组中删除,问合并数组中相同数字构成的最长子串是多长
思路:找到a中每个元素的最长连续相同子串,再找到b里面每个元素的最长连续相同子串,然后枚举所有元素的最长相同子串,然后答案取最大值
数据范围:1 ≤ n ≤ 2e5, 1 ≤ ai ≤ 2n, 1 ≤ bi ≤ 2n

#include <iostream>
#include <map>

using namespace std;

const int N = 2e5 + 10;
int a[N], b[N];

int main()
{
    int T; cin >> T;
    while (T --)
    {
        int n; cin >> n;
        map<int, int> m1, m2;
        for (int i = 0; i < n; i ++) 
        {
            cin >> a[i];
            m1[a[i]] = 1;
        }
        for (int i = 0; i < n; i ++) 
        {
            cin >> b[i];
            m2[b[i]] = 1;
        }
        for (int i = 0; i < n; i ++)
        {
            int j = i;
            if (a[i] == a[j])
            {
                while (j < n && a[i] == a[j]) j ++;
                m1[a[i]] = max(m1[a[i]], j - i);
                i = j - 1;
            }
        }
        for (int i = 0; i < n; i ++)
        {
            int j = i;
            if (b[i] == b[j])
            {
                while (j < n && b[i] == b[j]) j ++;
                m2[b[i]] = max(m2[b[i]], j - i);
                i = j - 1;
            }
        }
        int ans = 0;
        for (int i = 0; i <= 2 * n; i ++) ans = max(ans, m1[i] + m2[i]);
        cout << ans << endl;


    }
    return 0;
}

problem C. Copil Copac Draws Trees
吐槽:这题刷新了我单题错误类型数量,WA, TLE, MLE都出现了。。。
题目大意:给定n个点,n - 1条边,一个人按照如下方式画树,1:选定节点1开始,2:扩展从一个已经画好的节点开始扩展,3:检查是否绘制了所有点,没有则返回第一步。问这个人需要重复多少次这样的操作才可以画好树
思路:比赛的时候第一时间想到并查集,但并查集会tle,可以使用类似于下面给出的数据进行hack
5
4 5
3 4
2 3
1 2
由于每次都要遍历一遍所有边,因此时间复杂度是O(n^2)
正解是dfs,每次存一下扩展点的操作步数cnt,然后用f表示走到该边的代价,如果步数cnt[i] < cnt[father]那么说明要多走一步,否则则可以在f[father]的代价内完成

在参考了大佬写的之后发现了一种更为简单的写法,对于这题来说vector有着更可观的效率

#include <iostream>
#include <cstring>
#include <vector>

using namespace std;
typedef pair<int, int> PII;
const int N = 2e5 + 10, M = 2 * N;
vector<PII> e[N];
// int e[M], ne[M], h[N], w[M], idx;
int res = 0;

// void add(int a, int b, int c)
// {
//     e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx ++;
// }

void dfs(int u, int father, int now, int tot)
{
    res = max(res, tot);
    for (auto i : e[u])
    {
        if (i.first == father) continue;
        if (i.second < now) dfs(i.first, u, i.second, tot + 1);
        else dfs(i.first, u, i.second, tot);
    }
    // for (int i = h[u]; ~i; i = ne[i])
    // {
    //     int ver = e[i];
    //     if (ver == father) continue;
    //     if (w[i] < now) dfs(ver, u, w[i], tot + 1);
    //     else dfs(ver, u, w[i], tot);
    // }
}
int main()
{
    ios::sync_with_stdio(false); cin.tie(0), cout.tie(0);
    int T; cin >> T;
    while (T --)
    {
        res = 0;
        // res = idx = 0;
        // memset(h, -1, sizeof h);
        int n; cin >> n;
        for (int i = 1; i <= n; i ++) e[i].clear();
        for (int i = 0; i < n - 1; i ++)
        {
            int x, y; cin >> x >> y;
            // add(x, y, i), add(y, x, i);
            e[x].push_back({y, i});
            e[y].push_back({x, i});
        }
        dfs(1, 0, 0, 1);
        cout << res << endl;
    }
    return 0;
}

先给出并查集写法

#include <iostream>
#include <map>

using namespace std;

const int N = 2e5 + 10;
int p[N];
struct Edge
{
    int a, b;
}e[N];


int find(int x)
{
    if (x != p[x]) p[x] = find(p[x]);
    return p[x];
}
bool check(int n)
{
    for (int i = 1; i <= n; i ++) 
    {
        if (p[i] != 1) return false;
    }
    return true;
}

int main()
{
    int T; cin >> T;
    while (T --)
    {
        int n; cin >> n;
        for (int i = 1; i <= n; i ++) p[i] = i;
        for (int i = 0; i < n - 1; i ++) cin >> e[i].a >> e[i].b;
        int ans = 0;
        while (true)
        {
            if (check(n)) 
            {
                cout << ans << endl;
                break;
            }
            bool flag = true;
            for (int i = 0; i < n - 1; i ++)
            {
                int k1 = find(e[i].a), k2 = find(e[i].b);
                if (k1 == 1 && k2 == 1) continue;
                if (k1 != k2 && (k1 == 1 || k2 == 1))
                {
                    if (k1 == 1) p[k2] = k1;
                    else p[k1] = k2;
                }
            }
            // for (int i = 1; i <= n; i ++) cout << p[i] << " ";
            ans ++;
        }


    }
    return 0;
}

dfs写法

#include <iostream>
#include <vector>
#include <cstring>
#include <algorithm>

using namespace std;
const int N = 2e5 + 10;
vector<int> e[N];
vector<int> a(N), b(N), fa(N), cnt(N), f(N);

void dfs(int u, int father)
{
    fa[u] = father;
    for(auto i : e[u])
    {
      if(i == father) continue;
      dfs(i, u);
    }
}
void dfs1(int u, int father)
{
    for(auto i : e[u])
    {
        if(i == father) continue;
        else if(cnt[i] < cnt[u])
        {
            f[i] = f[u] + 1;
            dfs1(i, u);
        }
        else
        {
            f[i] = f[u];
            dfs1(i, u);
        }
    }
}

int main()
{
    ios::sync_with_stdio(false); cin.tie(0), cout.tie(0);
    int T; cin >> T;
    while(T --)
    {
        int n;
        cin >> n;
        for (int i = 1; i <= n; i ++) e[i].clear();
        for(int i = 0; i < n - 1; i ++)
        {
            int u, v; cin >> u >> v;
            a[i] = u, b[i] = v;
            e[u].push_back(v);
            e[v].push_back(u);
        }
        fa.clear();
        dfs(1, 0);
        cnt.clear();
        for(int i = 0; i < n - 1; i ++)
        {
            if(fa[a[i]] == b[i]) swap(a[i], b[i]);
            cnt[b[i]] = i;
        }
        f.clear();
        f[1] = 1;
        dfs1(1, 0);
        int res = 0;
        for(int i = 1;i <= n; i ++) res = max(res, f[i]);
        cout << res << endl;
    }
    return 0;
}

链式向前星加bfs写法

#include <iostream>
#include <queue>
#include <cstring>
#include <algorithm>
#include <vector>

using namespace std;
const int N = 2e5 + 10, M = 2 * N;
int e[M], ne[M], h[N], idx;
int a[N], b[N], fa[N];
int cnt[N], f[N];

void add(int a, int b)
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}

void dfs(int u, int father)
{
    fa[u] = father;
    for (int i = h[u]; ~i; i = ne[i])
    {
        int ver = e[i];
        if (ver == father) continue;
        dfs(ver, u);
    }
}

void bfs(int u)
{
    queue<int> q;
    q.push(u);
    while (!q.empty())
    {
        auto t = q.front();
        q.pop();
        for (int i = h[t]; ~i; i = ne[i])
        {
            int ver = e[i];
            if (f[ver]) continue;
            else if (cnt[ver] < cnt[t])
            {
                f[ver] = f[t] + 1;
                q.push(ver);
            }
            else
            {
                f[ver] = f[t];
                q.push(ver);
            }
        }
    }
}

int main()
{
    ios::sync_with_stdio(false); cin.tie(0), cout.tie(0);
    int T; cin >> T;
    while (T --)
    {
        idx = 0;
        memset(h, -1, sizeof h);
        int n; cin >> n;
        for (int i = 0; i < n - 1; i ++)
        {
            cin >> a[i] >> b[i];
            add(a[i], b[i]), add(b[i], a[i]);
        }
        dfs(1, 0);
        for (int i = 0; i < n - 1; i ++)
        {
            if (fa[a[i]] == b[i]) swap(a[i], b[i]);
            cnt[b[i]] = i;
        }
        memset(f, 0, sizeof f);
        f[1] = 1;
        bfs(1);
        cout << *max_element(f + 1, f + 1 + n) << endl;
    }
    return 0;
}



不得不吐槽这期太离谱,到第二题就开始看不懂题了,还好是vp的,后续查了一下发现大家对这期的评价很差啊哈哈哈哈

problem A. Likes
题目大意:给定n个时刻,ai如果大于0表示第ai个人点了一个赞,否则就表示第ai个人移除了赞,一个人如果没点赞就不能移除赞,现在重新排列这n个数,输出两个序列,一个是这个1-n中每个时刻点赞的最大人数,第二个是1-n中每个时刻点赞的最少人数
思路:第一个序列我们让所有人都先点赞,到达峰值了再开始取消点赞,第二个我们就让一个人先点赞然后再取消,最后再输出没有取消的数量就可以了
数据范围 1 ⩽ n ⩽ 100 1 ⩽ |bi| ⩽ n

#include <iostream>
#include <unordered_map>

using namespace std;
const int N = 110;

int main()
{
    ios::sync_with_stdio(false); cin.tie(0), cout.tie(0);
    int T;
    cin >> T;
    while (T --)
    {
        int n; cin >> n;
        unordered_map<int, int> dict;
        int cnt1 = 0, cnt2 = 0, cnt3 = 0;
        for (int i = 0; i < n; i ++)
        {
            int x; cin >> x;
            if (x > 0) 
            {
                cnt1 ++;
                if (dict[x]) cnt3 ++, dict[x] --;
                dict[x] = 1;
            }
            else 
            {
                cnt2 ++;
                if (dict[-x]) cnt3 ++, dict[x] --;
                dict[-x] = 1;
            }


        }
        for (int i = 1; i <= cnt1; i ++) cout << i << " ";
        for (int i = 1; i <= cnt2; i ++) cout << cnt1 - i << " ";
        cout << endl;
        for (int i = 0; i < cnt3; i ++) cout << 1 << " " << 0 << " ";
        for (int i = 1; i <= cnt1 - cnt3; i ++) cout << i << " ";
        cout << endl;
    }
}

problem B. Settlement of Guinea Pigs
题目大意:给定n个数,如果ai等于1,表示女孩买了一头猪,如果ai等于2,表示女孩找医生鉴定了所有猪的性别(如果不找医生就不知道猪的性别)女孩是个好人,为了不让猪有道德负担,所以必须杜绝公母一个笼子,我真的哭死,问最少需要多少笼子。
思路:描述非常阴间,事实上这题其实是求一个最大值,在不知道猪性别的时候需要不停的买笼子(真是浪费钱)找到医生鉴定之后可以把一些猪移到合适的笼子里去,但这时候空出来的笼子肯定不能退回去,所以最少需要的笼子数量并不会减少,每次找到医生之后可以让笼子数量缩小至pigs / 2 + 1,空出来的笼子可以装新买的猪
数据范围 1 ⩽ n ⩽ 1e5

#include <iostream>

using namespace std;
const int N = 1e5 + 10;
int a[N];

int main()
{
    ios::sync_with_stdio(false); cin.tie(0), cout.tie(0);
    int T;
    cin >> T;
    while (T --)
    {
        int n; cin >> n;
        for (int i = 0; i < n; i ++) cin >> a[i];
        int pigs = 0, cages = 0, ans = 0;
        for (int i = 0; i < n; i ++)
        {
            if (a[i] == 1)
            {
                pigs ++;
                cages ++;
            }
            else if (a[i] == 2 && pigs > 0) cages = pigs / 2 + 1;
            ans = max(ans, cages);
        }
        cout << ans << endl;
    }
}

problem C. The Very Beautiful Blanket
题目大意:给出n, m,让输出一个n * m的矩阵,使得矩阵中任意一个4 * 4的小矩阵都满足左上2 * 2小矩阵异或和等于右下2 * 2小矩阵异或和,右上2 * 2小矩阵异或和等于左下小矩阵异或和,同时还得输出矩阵中最多有多少不同的元素
思路:我们可以考虑把所有2 * 2矩阵的异或和都构造成0,构造方式:由于n <= 200,所以我们第一行输出0 - m,然后此后每行加上2^8即可
数据范围4 ≤ n, m ≤ 200

#include <iostream>

using namespace std;

const int N = 210;

int a[N];

int qmi(int a, int b)
{
    int res = 1;
    while (b)
    {
        if (b & 1) res = res * a;
        a = a * a;
        b >>= 1;
    }
    return res;
}

int main() 
{
    ios::sync_with_stdio(false); cin.tie(0), cout.tie(0);
    int T;
    cin >> T;
    while(T --)
    {
        int n, m;
        cin >> n >> m;
        cout << n * m << endl;
        for(int i = 1;i <= m; i ++)
        {
            a[i] = i - 1;
            cout << a[i] << " ";
        }
        cout << endl;
        for(int i = 2; i <= n; i ++)
        {
            for(int j = 1; j <= m; j ++) 
            {
                a[j] += qmi(2, 8);
                cout << a[j] << " ";
            }
            cout << endl;
        }
    }
    return 0;
}

problem D. Buying gifts
题目大意:给定一个数字n表示有n个店,然后输入两个数,表示ai礼物和bi礼物,每经过一个商店你必须买一个礼物,问买完之后ai和bi的最大值的绝对值之差最小是多少
思路:可以开一个PII类型的数组,存入ai,bi,然后对这个数组进行排序,我们就会惊奇的发现,答案在两个区间里找,比如我们现在走到i号店了,答案可以在[0, i - 1]号店里随便找一个bx,找到一个bx我们其他全买a,也不会影响最后a的最大值,但是注意,mx[i]小于ai的时候才可以这样做,同时答案也可能在后面,也就是[i + 1, n - 1]的范围内,在这个范围内我们需要找到一个bx的最大值来看看能不能更新ans
数据范围 2 ≤ n ≤ 500000 0 ≤ ai, bi ≤ 1e9

#include <iostream>
#include <algorithm>
#include <set>

using namespace std;
typedef pair<int, int> PII;


void solve() 
{
    int n; cin >> n;
    PII a[n + 1];   
    for (int i = 0; i < n; i ++) cin >> a[i].first >> a[i].second;
    sort(a, a + n);
    int mx[n + 1];
    mx[n - 1] = -2e9;
    for (int i = n - 2; i >= 0; i --) mx[i] = max(mx[i + 1], a[i + 1].second);//mx[i]表示i之后b商品的最大值是多少
    int ans = 2e9;
    set <int> s;
    for (int i = 0; i < n; i ++) 
    {
        ans = min(ans, abs(a[i].first - mx[i]));
        if (a[i].first > mx[i]) 
        {           
            // set<int>::iterator it = s.lower_bound(a[i].first);
            auto it = s.upper_bound(a[i].first);//才不会告诉你upper_bound和lower_bound其实都可以过
            if (it != s.end())   ans = min(ans, *it - a[i].first);//找到set中第一个大于等于a[i].first的更新答案
            if (it != s.begin()) ans = min(ans, a[i].first - *(--it));//找到set中第一个小于a[i].first的更新答案
        }
        s.insert(a[i].second);
    }
    cout << ans << endl;
}


int main() 
{    
    ios::sync_with_stdio(false); cin.tie(0), cout.tie(0);
    int T; cin >> T;
    while (T --) solve();

    return 0;
}

problem E&&F&&G
等变强了再做。。。



分享 STL总结

由于做了更多的题,对STL的用法也稍微有了一些领悟,结合y总的教案小小的总结一下
1

#include <vector>

1.1 声明

1 vector<int> a //相当于一个长度动态变化的int数组
2 vector<int> a(n) 
/**
*跟第一个类似,可以在读入时直接cin >> a[i],
*同时长度为n的数组初始为0,避免在声明局部变量数组的时候需要将所有数组元素初始成0
*vector<int> a(n, x) 表示将长度为n的a[i]赋成x
*/
3 vector<int> a[n] //声明一个一维长n,第二位长度动态变化的int数组
4 struct rec{...} vector<rec> a // 自定义的结构体类型也可以保存在vector中

1.2 主要作用

1 直接当数组用
2 离散化
3 高精度
4 存图
/**
* vector<int> e;
* for (int i = 0; i < n; i ++) 
* {
*     int a, b; cin >> a >> b;
*     e[a].push_back(b);
*     e[b].push_back(a);//无向图存储
*  }
*/

1.3 主要函数

vector<int> a;
1 a.size() //返回a中元素数量
2 a.empty() //返回a中是否有元素,若没有返回true,有返回false
3 a.push_back(x) //将x插入a的尾部
4 a.pop_back() //删除a的最后一个元素
5 a.clear() //将a清空,如果a是多维的,需要一维一维清空
6 a.begin() //返回a中第一个元素的迭代器
7 a.end() //返回a的尾部,但注意这是越界访问,相当于访问a[n],其中n = a.size()
8 a.front() //返回a中的第一个元素,等价于a[0], *a.begin()
9 a.back() //返回a中的最后一个元素,等价于a[n - 1], *(--a.end()),其中n = a.size()

1.4 迭代器

和指针类似,如果需要访问该迭代器指向的数,需要加*解除引用
类型 vector<int> :: iterator //对于vector<int> 而言
vector的迭代器是“随机访问迭代器”,可以把vector的迭代器与一个整数相加减,其行为和指针的移动类似。
可以把vector的两个迭代器相减,其结果也和指针相减类似,得到两个迭代器对应下标之间的距离。

1.5 遍历方式

for (int i = 0; i < a.size(); i ++)
    cout << a[i] << endl;

for (vector<int>::iterator it = a.begin(); it != a.end(); it ++)
    cout << *it << endl;

2

include <queue>

2.1 声明

queue<int> q;
struct rec{…}; queue<rec> q;
priority_queue<int> q;                              // 大根堆,若在里面存放结构体,需要重新定义小于号
priority_queue<int, vector<int>, greater<int>> q;   // 小根堆,若存放结构体,需要重新定义大于号
priority_queue<pair<int, int>> q;
下面给出存放结构体的示范
大根堆
#include <iostream>
#include <algorithm>
#include <queue>

using namespace std;
struct e
{
    int x, y;
    bool operator < (const e & W) const
    {
        return y < W.y;
    }
};

int main()
{
    priority_queue<e> q;
    q.push({1, 2}), q.push({2, 4});
    cout << q.top().y << endl;
}
小根堆
#include <iostream>
#include <algorithm>
#include <queue>

using namespace std;
struct e
{
    int x, y;
    bool operator > (const e & W) const
    {
        return y > W.y;
    }
};

int main()
{
    priority_queue<e, vector<e>, greater<e>> q;
    q.push({1, 2}), q.push({2, 4});
    cout << q.top().y << endl;
}

2.2 主要作用

1 队列主要用于bfs,spfa
2 优先队列中的小根堆用于dijkstra最短路,每次弹出队列中最小的元素
3 大根堆用于一些比较特殊的dijkstra,例如求最大路的时候会用,可参考1026最小花费
4 某些CF上的思维题会用

2.3 主要函数

queue
push    // 从队尾插入
pop     // 从队头弹出
front   // 返回队头元素
back    // 返回队尾元素
priority_queue
push    // 把元素插入堆
pop     // 删除堆顶元素
top     // 查询堆顶元素(最大值)

3

#include <stack>

3.1 主要作用

目前只发现可以进行括号匹配,后续再完善

3.2 主要函数

push    // 向栈顶插入
pop     // 弹出栈顶元素

4

#include <deque>

4.1 主要用途

spfa的SLF优化(只是对数据进行优化,想卡还是可以卡掉)
双端队列广搜
在边权只有0,1时可以用deque代替queue实现bfs,性能优于priority_queue
目前还没遇到其他的,待补充

4.2 主要函数

[]              // 随机访问
begin/end       // 返回deque的头/尾迭代器
front/back      // 队头/队尾元素
push_back       // 从队尾入队
push_front      // 从队头入队
pop_back        // 从队尾出队
pop_front       // 从队头出队
clear           // 清空队列

5

#include <set>

头文件set主要包括set和multiset两个容器,分别是“有序集合”和“有序多重集合”,即前者的元素不能重复,而后者可以包含若干个相等的元素。set和multiset的内部实现是一棵红黑树,它们支持的函数基本相同。
5.1 声明

set<int> s;
struct rec{…}; set<rec> s;  // 结构体rec中必须定义小于号
multiset<double> s;

5.2 主要用途

思维题
待完善

5.3 主要函数

5.2 size/empty/clear
与vector类似

5.3 迭代器
set和multiset的迭代器称为“双向访问迭代器”,支持星号*解除引用。仅支持++和--两个与算术相关的操作。
注意这玩意不支持随机访问,不支持随机访问,也就是说s[i]是不对的

设it是一个迭代器,例如set<int>::iterator it;

若把it ++,则it会指向“下一个”元素。这里的“下一个”元素是指在元素从小到大排序的结果中,排在it下一名的元素。
同理,若把it --,则it将会指向排在“上一个”的元素。

5.4 begin/end
返回集合的首、尾迭代器,时间复杂度均为 O(1)

s.begin()是指向集合中最小元素的迭代器。

s.end()是指向集合中最大元素的下一个位置的迭代器。换言之,就像vector一样,是一个“前闭后开”的形式。
因此s.end()是指向集合中最大元素的迭代器。

5.5 insert
s.insert(x)把一个元素x插入到集合s中,时间复杂度为 O(logn)

在set中,若元素已存在,则不会重复插入该元素,对集合的状态无影响。

5.6 find
s.find(x)在集合s中查找等于x的元素,并返回指向该元素的迭代器。若不存在,则返回s.end()。
时间复杂度为 O(logn)

5.7 lower_bound/upper_bound
这两个函数的用法与find类似,但查找的条件略有不同,时间复杂度为 O(logn)

s.lower_bound(x)查找大于等于x的元素中最小的一个,并返回指向该元素的迭代器。

s.upper_bound(x)查找大于x的元素中最小的一个,并返回指向该元素的迭代器。
注意这俩不能学数组一样减一个s.begin(),不然会报错
5.8 erase
设it是一个迭代器,s.erase(it)从s中删除迭代器it指向的元素,时间复杂度为 O(logn)

设x是一个元素,s.erase(x)从s中删除所有等于x的元素,时间复杂度为 O(k+logn),其中 k是被删除的元素个数。

5.9 count
s.count(x)返回集合s中等于x的元素个数,时间复杂度为 O(k+logn),其中 k 为元素x的个数。

6

#include <map>

map容器是一个键值对key-value的映射,其内部实现是一棵以key为关键码的红黑树。Map的key和value可以是任意类型,其中key必须定义小于号运算符。
6.1 声明

map<key_type, value_type> name;

//例如:
map<long long, bool> vis;
map<string, int> hash;
map<pair<int, int>, vector<int>> test;

6.2 主要作用

1 记录路径,例如提高课中的八数码
2 思维题

6.3 主要函数

6.2 size/empty/clear/begin/end
均与set类似。

6.3 insert/erase
与set类似,但其参数均是pair<key_type, value_type>。

6.4 find
h.find(x)在变量名为h的map中查找key为x的二元组。

6.5 []操作符
h[key]返回key映射的value的引用,时间复杂度为 O(logn)

[]操作符是map最吸引人的地方。我们可以很方便地通过h[key]来得到key对应的value。
同时可以对h[key]进行赋值操作,改变key对应的value。

7

#include <unordered_map>

与map很像,区别是map对于insert的变量会自动排序,而unordered_map不会,查找效率非常高可以达到O(1),但缺点是,unordered_map是可以被卡的,如果制造很多哈希冲突就会导致查询效率非常低,所以貌似CF大佬都喜欢用map



活动打卡代码 AcWing 361. 观光奶牛

#include <iostream>
#include <queue>
#include <cstring>

using namespace std;

const int N = 1010, M = 5010;
int f[N];
int e[M], ne[M], h[N], w[M], idx;
double dist[N];
int cnt[N];
bool st[N];
int n, m; 


void add(int a, int b, int c)
{
    e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx ++;
}

bool check(double mid)
{
    queue<int> q;
    for (int i = 1; i <= n; i ++)
    {
        cnt[i] = 0;
        q.push(i);
        st[i] = true;
    }
    while (!q.empty())
    {
        auto t = q.front();
        q.pop();
        st[t] = false;
        for (int i = h[t]; ~i; i = ne[i])
        {
            int ver = e[i];
            double x = w[i] * mid - f[t];
            if (dist[ver] > dist[t] + x)
            {
                dist[ver] = dist[t] + x;
                cnt[ver] = cnt[t] + 1;
                if (cnt[ver] >= n) return true;
                if (!st[ver])
                {
                    st[ver] = true;
                    q.push(ver);
                }
            }
        }
    }
    return false;
}

int main()
{
    ios::sync_with_stdio(false); cin.tie(0), cout.tie(0);
    memset(h, -1, sizeof h);
    cin >> n >> m;
    for (int i = 1; i <= n; i ++) cin >> f[i];
    while (m --)
    {
        int a, b, c; cin >> a >> b >> c;
        add(a, b, c);
    }
    double l = 0, r = 1e6, eps = 1e-4;
    while (r - l > eps)
    {
        double mid = (l + r) / 2;
        if (check(mid)) l = mid;
        else r = mid;
    }
    printf("%.2lf", l);
    return 0;
}


活动打卡代码 AcWing 904. 虫洞

#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>

using namespace std;

const int N = 510, M = 5210;
int e[M], ne[M], h[N], w[M], idx;
int dist[N], cnt[N];
bool st[N];
int n, m1, m2;

void add(int a, int b, int c)
{
    e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx ++;
}

bool spfa()
{
    memset(cnt, 0, sizeof cnt);
    memset(st, 0, sizeof st);
    memset(dist, 0x3f, sizeof dist);
    queue<int> q;
    for (int i = 1; i <= n; i ++)
    {
        q.push(i);
        st[i] = true;
    }
    while (!q.empty())
    {
        auto t = q.front();
        q.pop();
        st[t] = false;
        for (int i = h[t]; ~i; i = ne[i])
        {
            int ver = e[i];
            if (dist[ver] > dist[t] + w[i])
            {
                dist[ver] = dist[t] + w[i];
                cnt[ver] = cnt[t] + 1;
                if (cnt[ver] >= n) return true;
                if (!st[ver])
                {
                    q.push(ver);
                    st[ver] = true;
                }
            }
        }
    }
    return false;
}

int main()
{
    ios::sync_with_stdio(false); cin.tie(0), cout.tie(0);
    int T; cin >> T;
    while (T --)
    {
        memset(h, -1, sizeof h);
        idx = 0;
        cin >> n >> m1 >> m2;
        while (m1 --)
        {
            int a, b, c; cin >> a >> b >> c;
            add(a, b, c), add(b, a, c);
        }
        while (m2 --)
        {
            int a, b, c; cin >> a >> b >> c;
            add(a, b, -c);
        }
        if (spfa()) cout << "YES" << endl;
        else cout << "NO" << endl;
    }
    return 0;
}



problem A 最小的数字
题目大意:给一个n,求出最小整数x,使得x >= n且x可以被3整除
思路:太简单了,略
数据范围:0 <= n <= 1e9

#include <iostream>

using namespace std;

int main()
{
    ios::sync_with_stdio(false); cin.tie(0), cout.tie(0);
    int n; cin >> n;
    int k = n / 3;
    if (n % 3 == 0) cout << k * 3 << endl;
    else cout << k * 3 + 3 << endl;
    return 0;
}

problem B 优美的GCD
题目大意:给出一个n,求两个不同的数使得它们的gcd是n
思路:一个等于n,一个等于2 * n即可
数据范围 1 <= n <= 1e6

#include <iostream>

using namespace std;

int main()
{
    ios::sync_with_stdio(false); cin.tie(0), cout.tie(0);
    int T; cin >> T;
    while (T --)
    {
        int n; cin >> n;
        cout << n << " " << 2 * n << endl;
    }
    return 0;
}

problem C 优美的序列
题目大意:给定一个序列,对其进行任意操作使其变成优美的序列,优美序列满足对于任意i, j而言abs(a[i] - a[j]) >= abs(i - j),输出优美序列
思路:个人认为非常像codeforces 860 DIV2 A. Showstopper,采用贪心的策略,越大的越应该放到后面

#include <iostream>
#include <algorithm>

using namespace std;
const int N = 1010;
int a[N];

int main()
{
    ios::sync_with_stdio(false); cin.tie(0), cout.tie(0);
    int T; cin >> T;
    while (T --)
    {
        int n; cin >> n;
        for (int i = 1; i <= n; i ++) cin >> a[i];
        sort(a + 1, a + n + 1);
        bool suc = true;
        for (int i = 1; i <= n; i ++)
        {
            for (int j = i; j <= n; j ++)
            {
                if (abs(a[i] - a[j]) < abs(i - j)) 
                {
                    suc = false;
                    break;
                }
            }
            if (!suc) break;
        }
        if (suc) for (int i = 1; i <= n; i ++) cout << a[i] << " ";
        else cout << -1;
        cout << endl;
    }
    return 0;
}

赛后补题
problem D&E Kevin喜欢零
题目大意:给出一个序列a和一个整数k,对于一个区间[l, r]而言,x = al * a(l + 1) * a(l + 2) * … * ar,x后缀0的个数刚好为k,求满足要求的区间个数
思路:对于D而言,可以预处理前缀积,然后二分求答案,重点写一下E的,看题解都看了大半天,难受呜呜呜,首先对于x而言,我们可以得到x = p * qmi(10, k) p不能被10整除,因此得到x = p * qmi(2, k) * qmi(5, k),我们令ca[i]等于ai中质因子2的数量,cb[i]等于ai中质因子5的数量, 我们不难发现,假设n个数的乘积为q, k = min(ca[q], cb[q]),例如125 * 8中k等于3,因此我们预处理出ca的前缀和和cb的前缀和,然后枚举一下每一个边界l会有多少中情况,接着将l除去,再看后面的边界(注意,除去l的操作非常神奇,头一次见)

#include <iostream>
#include <algorithm>
#define int long long

using namespace std;
const int N = 2e5 + 10;
int n, k;
int a[N];
int ca[N], cb[N];
void solve()
{
    cin >> n >> k; 
    for(int i = 0; i <= n; i ++) ca[i] = cb[i] = 0;
    for(int i = 1; i <= n; i ++) cin >> a[i];
    for(int i = 1; i <= n; i ++)
    {
        int u = a[i];
        while(u % 2 == 0) u /= 2, ca[i] ++;
        while(u % 5 == 0) u /= 5, cb[i] ++;
    }
    for(int i = 1; i <= n; i ++)
    {
        ca[i] += ca[i - 1];
        cb[i] += cb[i - 1];
    }
    int ans = 0;
    // for (int i = 1; i <= n; i ++) cout << ca[i] << " ";
    // cout << endl;
    // for (int i = 1; i <= n; i ++) cout << cb[i] << " ";
    // cout << endl;
    //注意这里前缀和一定是单调递增的,因此必定存在r>=l
    for(int l = 1; l <= n; l ++)
    {
        int v1 = k + ca[l - 1];//将前一个枚举的边界除去,这里通过让k + ca, k + cb除去,其实和除一个数就在另一边乘这个数是一个道理
        int v2 = k + cb[l - 1];
        int l1 = lower_bound(ca + 1, ca + 1 + n, v1) - ca;//找到大于等于v1的第一个下标
        // cout << "l1 = " << l1 << "  " << v1 << endl;
        int r1 = upper_bound(ca + 1, ca + 1 + n, v1) - ca - 1;
        // cout << "r1 = " << r1 << endl;
        //upper_bound用于找到大于v1的第一个数,-1作用在于找到小于等于v1的最后一个下标
        int l2 = lower_bound(cb + 1, cb + 1 + n, v2) - cb;//找到大于等于v2的第一个下标
        int r2 = upper_bound(cb + 1, cb + 1 + n, v2) - cb - 1;//找到小于等于v2的最后一个下标
        // cout << "l2 = " << l2 << "  " << v2 << endl;
        // cout << "r2 = " << r2 << endl;
        int L = max(l1, l2);
        int R = max(r1, r2);
        // cout << "L = " << L << " " << " R = " << R << endl;
        if(L > R) continue;//说明L越界了,此处也可以写成L == n + 1
        L = max(L, l);
        ans += (R - L + 1);
        // cout << "ans = " << ans << endl;
    }
    cout << ans << endl;
} 
signed main()
{
    ios::sync_with_stdio(0); cin.tie(0), cout.tie(0);
    int T; cin >> T; 
    while(T --) solve();
    return 0;
}



#include <iostream>
#include <algorithm>
#include <cstring>
#define int long long

using namespace std;

const int N = 510, M = 1e4 + 10;
struct Edge
{
    int x, y, w;
    bool f;
    bool operator < (const Edge &W) const
    {
        return w < W.w;
    }
}edges[M];
int p[N];
int dist1[N][N], dist2[N][N];
int e[2 * M], ne[2 * M], w[2 * M], h[N], idx;
int n, m;

void add(int a, int b, int c)
{
    e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx ++;
}

int find(int x)
{
    if (x != p[x] && p[x] != p[p[x]]) p[x] = find(p[x]);
    return p[x];
}

void dfs(int u, int fa, int maxd1, int maxd2, int d1[], int d2[])
{
    d1[u] = maxd1, d2[u] = maxd2;
    for (int i = h[u]; ~i; i = ne[i])
    {
        int ver = e[i];
        if (ver != fa)
        {
            int td1 = maxd1, td2 = maxd2;
            if (w[i] > td1) td2 = td1, td1 = w[i];
            else if (w[i] < td1 && w[i] > td2) td2 = w[i];
            dfs(ver, u, td1, td2, d1, d2);
        }
    }
}

signed main()
{
    ios::sync_with_stdio(false); cin.tie(0), cout.tie(0);
    cin >> n >> m;
    memset(h, -1, sizeof h);
    for (int i = 1; i <= n; i ++) p[i] = i;
    for (int i = 0; i < m; i ++) cin >> edges[i].x >> edges[i].y >> edges[i].w;
    sort(edges, edges + m);
    int sum = 0;
    for (int i = 0; i < m; i ++)
    {
        int k1 = find(edges[i].x), k2 = find(edges[i].y);
        if (k1 != k2)
        {
            p[k1] = k2;
            sum += edges[i].w;
            add(edges[i].x, edges[i].y, edges[i].w);
            add(edges[i].y, edges[i].x, edges[i].w);
            edges[i].f = true;
        }
    }
    for (int i = 1; i <= n; i ++) dfs(i, -1, -1e9, -1e9, dist1[i], dist2[i]);
    int res = 1e18;
    for (int i = 0; i < m; i ++ )
    {
        if (!edges[i].f)
        {
            int a = edges[i].x, b = edges[i].y, w = edges[i].w;
            int t;
            if (w > dist1[a][b])
                t = sum + w - dist1[a][b];
            else if (w > dist2[a][b])
                t = sum + w - dist2[a][b];
            res = min(res, t);
        }
    }
    cout << res << endl;
    return 0;
}



气炸了,昨晚半个小时写了三个题,结果第四个题看不懂题,磨叽大半小时后终于大概理解什么意思了,然后又写不出来,真是操蛋了

problem A. Grasshopper on a Line
题目大意:给定一个坐标n,然后再给一个k,从0开始跳,每次跳的距离不能被k整除,问最多跳几次到n,并输出每次跳的距离,如果有多个任意输出一个即可
思路:对于不能被k整除的n直接输出1即可,对于能被k整除的n,先跳n - 1步,再跳1步即可
数据范围: 1 ≤ n ≤ 100; 2 ≤ k ≤ 100

#include <iostream>

using namespace std;

int main()
{
    int T; cin >> T;
    while (T --)
    {
        int n, k; cin >> n >> k;
        if (n % k)
        {
            cout << 1 << endl;
            cout << n << endl;
        }
        else
        {
            cout << 2 << endl;
            cout << n - 1 << " " << 1 << endl; 
        }
    }
    return 0;
}

problem B. Comparison String
题目大意:给定一个只包含>和<的字符串,让你构造一个数组,对于i位置上的<,要求si < s(i + 1),对于i位置上的>,要求si > s(i + 1),问构造出的数组最少能有几种不同的元素
例如,对于<<>>而言,可以构造12321符合要求,且只有三个不同元素
思路:找到最长连续的>和<,在两个之间取一个max再加1就是答案
数据范围:1 ≤ n ≤ 100

#include <iostream>

using namespace std;

int main()
{
    int T; cin >> T;
    while (T --)
    {
        int n; cin >> n;
        string str; cin >> str;
        int cnt1 = 0, cnt2 = 0;
        for (int i = 0; i < n; )
        {
            if (str[i] == '>') 
            {
                int cnt3 = 0;
                while (i < n && str[i] == '>') i ++, cnt3 ++;
                cnt1 = max(cnt1, cnt3);
            }
            else
            {
                int cnt4 = 0;
                while (i < n && str[i] == '<') i ++, cnt4 ++;
                cnt2 = max(cnt2, cnt4);
            }
        }
        cout << max(cnt1, cnt2) + 1 << endl;
    }
    return 0;
}

problem C. Best Binary String
题目大意:给定字符串s,只包含‘1’, ‘0’, ‘?’,现在把问号替换成‘0’或‘1’,替换后,需要对字符串进行排序使得所有0在1前面,每次操作可以翻转一个子串,问怎么替换可以使操作最小
思路:每次可以翻转一个子串,也就是说我们希望整段一样的更多,可以使用一个flag,遇到‘0’变0,遇到‘1’变1,如果是问号就输出flag
数据范围 1 ≤ |s| ≤ 3e5

#include <iostream>

using namespace std;

int main()
{
    int T; cin >> T;
    while (T --)
    {
        string str; cin >> str;
        int flag = 0;
        for (int i = 0; i < str.size(); i ++)
        {
            if (str[i] == '0') 
            {
                flag = 0;
                cout << (char)str[i];
            }
            else if (str[i] == '1')
            {
                flag = 1;
                cout << (char)str[i];
            }
            else cout << (char)(flag + '0');
        }
        cout << endl;
    }
    return 0;
}

problem D. Bracket Coloring
不得不吐槽一下根本看不懂题,那个染色操作是真迷。。。
题目大意:给定字符串s,包含’(‘和’)’,把s拆分成一些子序列,使得子序列从左到右排都是合法括号序列,或从右到左排都是合法序列,如果可以请输出每个子序列的编号,否则输出-1
思路:其实只要’(‘的数量等于’)’的数量那么他们从左到右排或者从右到左排一定是美丽的,例如例子中的((())))(可以看作((()))是一个正排合法序列)(是一个反排合法序列,这里可以画图说明,当只在1和4中一个象限的时候输出1,否则输出2,我们可以通过第一个字符的方向选择象限,’(‘说明是1象限,否则是4象限
codeforces edu 149 D.png

数据范围:2 ≤ n ≤ 2e5

#include <iostream>

using namespace std;
const int N = 2e5 + 10;

int ans[N];

int main()
{
    int T; cin >> T;
    while(T --)
    {
        int n; cin >> n;
        string s; cin >> s;
        int cnt = 0, k = 0, lst = 0;
        for(int i = 0; i < n; i ++)
        {
            if(s[i] == s[0]) cnt ++;
            else cnt --;
            if(lst + cnt > 0) ans[i] = 1;
            else ans[i] = 2;
            k = max(k, ans[i]);
            lst = cnt;
        }
        if(cnt != 0)
        {
            cout << -1 << endl;
            continue;
        }
        cout << k << endl;
        for(int i = 0; i < n; i ++) cout << ans[i] << " ";
        cout << endl;
    }
    return 0;
}


活动打卡代码 AcWing 346. 走廊泼水节

#include <iostream>
#include <algorithm>

using namespace std;
const int N = 6010, M = N * N / 2;
struct Edge
{
    int x, y, w;
    bool operator < (const Edge &W) const
    {
        return w < W.w;
    }
}e[M];

int p[N], sz[N];
int n;

int find(int x)
{
    if (x != p[x] && p[x] != p[p[x]]) p[x] = find(p[x]);
    return p[x];
}

int kruskal()
{
    sort(e, e + n - 1);
    int res = 0;
    for (int i = 0; i < n - 1; i ++)
    {
        int k1 = find(e[i].x), k2 = find(e[i].y);
        if (k1 != k2)
        {
            res += (sz[k1] * sz[k2] - 1) * (e[i].w + 1);
            sz[k2] += sz[k1];
            p[k1] = k2;
        }
    }
    return res;
}

int main()
{
    ios::sync_with_stdio(false); cin.tie(0), cout.tie(0);
    int T; cin >> T;
    while (T --)
    {
        cin >> n;
        for (int i = 0; i < n - 1; i ++) cin >> e[i].x >> e[i].y >> e[i].w;
        for (int i = 1; i <= n; i ++) p[i] = i, sz[i] = 1;
        int ans = kruskal();
        cout << ans << endl;
    }
    return 0;
}


活动打卡代码 AcWing 1145. 北极通讯网络

#include <iostream>
#include <algorithm>
#include <cmath>

using namespace std;

const int N = 510, M = N * N;
typedef pair<int, int> PII;
struct Edge
{
    int x, y;
    double w;
    bool operator < (const Edge &W) const
    {
        return w < W.w;
    }
}e[M];
PII q[N];
int p[N];
int n, k, cnt;

double get_dist(PII a, PII b)
{
    int x = a.first - b.first;
    int y = a.second - b.second;
    return sqrt(x * x + y * y);
}

int find(int x)
{
    if (x != p[x] && p[x] != p[p[x]]) p[x] = find(p[x]);
    return p[x];
}

double kruskal()
{
    sort(e, e + cnt);
    double res = 0;
    for (int i = 0; i < cnt; i ++)
    {
        if (n <= k) break;
        int k1 = find(e[i].x), k2 = find(e[i].y);
        if (k1 != k2)
        {
            res = e[i].w;
            p[k1] = k2;
            n --;
        }
    }
    return res;
}

int main()
{
    cin >> n >> k;
    for (int i = 0; i <= n; i ++) p[i] = i;
    for (int i = 0; i < n; i ++) cin >> q[i].first >> q[i].second;
    for (int i = 0; i < n; i ++)
        for (int j = 0; j < i; j ++)
            e[cnt ++] = {i, j, get_dist(q[i], q[j])};
    double ans = kruskal();
    printf("%.2lf\n", ans);
    return 0;
}