CCF CSP 竞赛试题——区块链(201912-4)

这题有一个大坑:同一节点,同一时间,可能产生多个块。即,存在 a i   b i   c i a_i\ b_i\ c_i ai bi ci a j   b j   c j a_j\ b_j\ c_j aj bj cj ,其中 a i = a j , b i = b j a_i = a_j, b_i = b_j ai=aj,bi=bj ,这样的输入存在。( 1 ≤ i , j ≤ k 1 \le i, j \le k 1i,jk

题目中没有找到对这一点的说明,示例输入输出也没有体现。当然,题目也没有明示这样是不可能的。如果理解错误,将cre设定为map>的话,就只有80分。

#include 
using namespace std;

int n, m, t, k;
vector<vector<int>> G;                          // 邻接表方式存图
map<int, unordered_map<int, vector<int>>> rec;  // 收到的链,按‘时间-节点-链’组织
vector<vector<int>> links;                      // 当前各节点的主链
map<int, unordered_map<int, vector<int>>> cre;  // 创建的新块,按‘时间-节点-块号链’组织。同一时刻,同一节点,可以产生多个块

vector<int> input() {
     
    string line;
    getline(cin, line);
    istringstream iss(line);
    vector<int> res;
    int x = 0;
    while (iss >> x) res.push_back(x);
    return res;
}

// 节点idx在时刻time接收到链vec的处理过程
void update(int time, int idx, const vector<int>& vec) {
     
    auto& cur = links[idx];
    if (cur.size() < vec.size() || cur.size() == vec.size() && cur.back() > vec.back()) {
     
        cur = vec;

        // links[idx]更新了,将新链推送给邻居
        for (int nei: G[idx]) {
     
            auto& temp = rec[time + t][nei];
            // 邻居在同一时刻可能收到多个链,只需保留最长且最后一个块号最小的链
            if (temp.size() < cur.size() || temp.size() == cur.size() && temp.back() > cur.back()) {
     
                temp = cur;
            }
        }
    }
}

// 按时间顺序处理积攒的rec和cre操作,直至时刻T
void proceedUntil(int T) {
     
    auto rec_p = rec.begin(); // rec_p将始终指向rec的第一个元素,或rec.end(),如果rec为空
    auto cre_p = cre.begin(); // cre_p将始终指向cre的第一个元素,或cre.end(),如果cre为空
    while (rec_p != rec.end() || cre_p != cre.end()) {
     
        if (cre_p == cre.end() || rec_p != rec.end() && rec_p->first <= cre_p->first) {
      // 先处理接收到的链
            if (rec_p->first > T) break;
            for (const auto& ele: rec_p->second) {
     
                update(rec_p->first, ele.first, ele.second);
            }
            rec_p = rec.erase(rec_p);
        } else {
      // 再处理新建操作,两者都划归到update上
            if (cre_p->first > T) break;
            for (const auto& ele: cre_p->second) {
     
                auto temp = links[ele.first];
                temp.insert(temp.end(), ele.second.begin(), ele.second.end());
                update(cre_p->first, ele.first, temp);
            }
            cre_p = cre.erase(cre_p);
            rec_p = rec.begin(); // 调用update之后,rec可能被修改,rec_p可能因此而失效
        }
    }
}

// 加快cin和cout的速度
static const int _ = []() {
     
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    return 0;
}();

int main() {
     
    cin >> n >> m;
    G.resize(n + 1);
    links.resize(n + 1, {
     0});
    for (int i = 0; i < m; ++i) {
     
        int u, v;
        cin >> u >> v;
        G[u].push_back(v);
        G[v].push_back(u);
    }
    cin >> t >> k;
    cin.get(); // 去掉输入流里一个换行符
    while (k--) {
     
        vector<int> op = input();
        if (op.size() == 3) {
        // 创建块,将操作缓存起来,不立刻处理
            cre[op[1]][op[0]].push_back(op[2]);
        } else {
                     // 等查询主链时,才依次处理完时刻op[1]及其之前的操作
            proceedUntil(op[1]);
            cout << links[op[0]].size();
            for (int x: links[op[0]]) cout << " " << x; cout << endl;
        }
    }
    return 0;
}

附:

1.in

5 10
1 2
1 3
1 4
1 5
2 3
2 4
2 5
3 4
3 5
4 5
1 27
1 1 1
2 1 2
3 1 3
4 1 4
5 1 5
1 1
2 1
3 1
4 1
5 1
1 2
2 2
3 2
4 2
5 2
1 10 10
2 11 9
1 11
2 11
3 11
4 11
5 11
1 12
2 12
3 12
4 12
5 12

1.out

2 0 1
2 0 2
2 0 3
2 0 4
2 0 5
2 0 1
2 0 1
2 0 1
2 0 1
2 0 1
3 0 1 10
4 0 1 10 9
3 0 1 10
3 0 1 10
3 0 1 10
4 0 1 10 9
4 0 1 10 9
4 0 1 10 9
4 0 1 10 9
4 0 1 10 9

2.in

15 13
1 2
2 3
3 4
4 5
1 6
6 7
7 8
8 9
1 10
10 11
11 12
12 13
14 15
6 28
1 1 1
1 2 2
1 6
2 7
13 7
9 7
5 7
3 14
8 14
5 14
11 14
9 25
5 25
13 25
9 28 3
5 29 4
13 29 5
1 53
2 59 6
2 59
1 1000
3 1000
8 1000
9 1000
10 1000
13 1000
14 1000
15 1000

2.out

3 0 1 2
2 0 1
1 0
1 0
1 0
3 0 1 2
1 0
1 0
3 0 1 2
2 0 1
2 0 1
2 0 1
4 0 1 2 3
5 0 1 2 3 6
5 0 1 2 3 6
5 0 1 2 3 6
5 0 1 2 3 6
5 0 1 2 3 6
5 0 1 2 3 6
5 0 1 2 3 6
1 0
1 0

你可能感兴趣的:(CCF,CSP,竞赛试题)