首页 > 其他 > 详细

第一场1006

时间:2020-07-22 23:51:43      阅读:100      评论:0      收藏:0      [点我收藏+]

Finding a MEX

问题:

给一个无向图,0≤n,m≤1e5(n表示点,m表示边);

每个点有权值。有0≤q≤1e5次询问,2种询问方式如下:

  • type==1 u x 把u点的权值换成x
  • type==2 u 查询u点的mex值(mex(u)为所有于u点相邻的点他们的权值中第一个没有出现过的非负整数)

比如:u相连的点权值为{0,1,2,3,5},那么4就是第一个没有出现的值。mex(u)=4;

原题链接:http://acm.hdu.edu.cn/contests/contest_showproblem.php?pid=1006&cid=879

 

这道题是一道银牌题(没做出来)

思考:

怎么减小修改次数?

图得边已经固定了最多1e5,每一条边会产生2度。(u--v) u,v各增加1度。也就是最大为2e5度。

那其实计算每一个平均每个点能连接的度数为√2e5≈450。这个平均求出来的意义在于找出那些菊花点最多

有少个。平均是450,那么大于450度的点绝对不会超过450。然后题解给的350主要是没管度数是否×2。

(当然对结果不影响,由于我是根据题解的代码自己写的,下面用350讲解)

那: 2e5/350=571 即 超过350度的点不会大于571个点。那就好做了

我们把点分为两批。度数大于350的点我们把他作为大点。度数小于350的点我们左右小点。

  • 大点:
    • 初始时:只记录该点权值。
    • 在修改时:只更改其权值。
    • 查询和小点查询方式一样。
  • 小点:
    • 初始时:记录该点权值,并且将他所有相连的点都增加这个权值(用来计算mex,具体怎么记录后面讲)
    • 修改时:先把之前记录值所有点都删去之前的权值,在增加新的权值。
    • 查询时:由于一直没怎加大点的权值,这里把于查询点所有的大点的权值加入到该点。这样与之相连的大点(最多570)和其他小点(之前已经添加过的)其权值都知道了,那就求一个mex就是正确答案。求完过后把所有大点的权值去掉(570次)

由上述算法我们可以看到无论是查询还是修改我们的复杂度都小于600次复杂度也就为O(q√m)

哪还有查询和插入的复杂度没有计算。

这里我们用树状数组求mex。

树状数组求mex

首先给每一个点都分配一颗等于他度数大小的树状数组。

为什么是每一个等于他啦?想想mex是求第一个不会出现的整数。那度数为5的点,他的答案可能会超过5吗?他最大为5。前面把0 1 2 3 4占完了。

所以权值大于度数的值我们根本没有记录的意义。(这样在空间上就可行了)

其次怎么求?

我们把每个点的所有度数值在树状数组上加1。为什么要加1啦?

比如:u由5度。那对应的数组C[6]={0,1,1,1,1,1,1} 第一个为0是因为树状数组下标不能为0,无法修改,找到答案在-1就行了。

然后假设与权值为0 1 3的点相连,那么将这些点置0。那就变成了 {0 0 0 1 0 1 1},这样你就发现其实就等于树状数组第一个区间求和为1的下标。

这里就是3,答案为3-1=2.树状数组有种在logn求出第一个区间和为x(这里求mex,也就是x==1)的算法。有兴趣可以看看代码如何实现的。

到此位置。思路就讲完了,然后代码如下,结合起来看就应该比较容易了

#include <bits/stdc++.h>
using namespace std;
const int N = 110000;
const int M = 350;
int A[N];
vector<int> con[N], adj[N];
struct Query {
    int type, u, x;
    Query(int type = 0, int u = 0, int x = 0) : type(type), u(u), x(x) {}
} Q[N];
int Array[N * M * 2], hello[N * M * 2];
int *BIT[N], *frq[N], lim[N];
inline void add(int id, int u, int x) {
    for (int i = u; i <= lim[id]; i += i & -i) BIT[id][i] += x;
}
inline int get(int id, int u) {
    int ret = 0;
    for (int i = u; i > 0; i -= i & -i) ret += BIT[id][i];
    return ret;
}
inline int get_lower_bound(int id, int s) {
    int ret = 0;
    for (int i = 31 - __builtin_clz(lim[id]); i >= 0; i--) {
        ret ^= 1<<i;
        if (ret > lim[id]) {
            ret ^= 1<<i;
            continue;
        }
        s -= BIT[id][ret];
        if (s <= 0) {
            s += BIT[id][ret];
            ret ^= 1<<i;
        }
    }
    return ++ret;
}
int main() {
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    string s;
    cin>>s;
    
    int tcase; cin >> tcase;
    while (tcase--) {
        int n, m; cin >> n >> m;
        for (int i = 1; i <= n; i++) cin >> A[i];
        for (int i = 0; i < m; i++) {
            int u, v; cin >> u >> v;
            con[u].push_back(v), con[v].push_back(u);
        }
        
        for (int i = 1; i <= n; i++) {
            for (int v : con[i]) {
                if (con[v].size() >= M) adj[i].push_back(v);
            }
        }
        
        int q; cin >> q;
        
        for (int i = 0; i < q; i++) {
            cin >> Q[i].type >> Q[i].u;
            if (Q[i].type == 1) {
                cin >> Q[i].x;
            }
        }
        
        for (int i = 1; i <= n; i++) lim[i] = con[i].size() + 1;
        int *p = Array, *qq = hello;
        for (int i = 1; i <= n; i++) {
            BIT[i] = p, p += lim[i] + 1;
            frq[i] = qq, qq += lim[i] + 1;
        }
        
        for (int i = 1; i <= n; i++) for (int j = 1; j <= lim[i]; j++) add(i, j, 1);
        for (int i = 1; i <= n; i++) if (con[i].size() < M) {
            for (int v : con[i]) {
                int val = A[i];
                if (val <= lim[v]) {
                    if (!frq[v][val]) add(v, val + 1, -1);
                    frq[v][val]++;
                }
            }
        }
        
        for (int i = 0; i < q; i++) {
            if (Q[i].type == 1) {
                if (con[Q[i].u].size() < M) {
                    int val = A[Q[i].u];
                    for (int v : con[Q[i].u]) {
                        if (val <= lim[v]) {
                            frq[v][val]--;
                            if (!frq[v][val]) add(v, val + 1, 1);
                        }
                        if (Q[i].x <= lim[v]) {
                            if (!frq[v][Q[i].x]) add(v, Q[i].x + 1, -1);
                            frq[v][Q[i].x]++;
                        }
                    }
                    A[Q[i].u] = Q[i].x;
                } else {
                    A[Q[i].u] = Q[i].x;
                }
            } else {
                for (int v : adj[Q[i].u]) {
                    if (A[v] <= lim[Q[i].u]) {
                        if (!frq[Q[i].u][A[v]]) add(Q[i].u, A[v] + 1, -1);
                        frq[Q[i].u][A[v]]++;
                    }
                }
                
                cout << get_lower_bound(Q[i].u, 1) - 1 << "\n";
                
                for (int v : adj[Q[i].u]) {
                    if (A[v] <= lim[Q[i].u]) {
                        frq[Q[i].u][A[v]]--;
                        if (!frq[Q[i].u][A[v]]) add(Q[i].u, A[v] + 1, 1);
                    }
                }
            }
        }
        for (int i = 1; i <= n; i++) {
            con[i].clear(), adj[i].clear();
            for (int j = 0; j <= lim[i]; j++) BIT[i][j] = frq[i][j] = 0;
            lim[i] = 0;
        }
    }
    return 0;
}

 

代码是题解代码。比我自己写的要工整些,有问题欢迎指出。

 

 

 

 

第一场1006

原文:https://www.cnblogs.com/grisaia/p/13363307.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!