首页 > 其他分享 >省选图论专题

省选图论专题

时间:2024-12-23 20:41:59浏览次数:3  
标签:图论 专题 dist fa 省选 rep long int using

还没打完数学专题呢就来打这个了。(其实是不会多项式)

难度大概升序。

Giving Awards

注意到一个关键信息:每对人只会被提到一次。所以一定有解。

考虑卡bug,假如\(a\)欠\(b\)钱,那么就先让\(b\)取钱再让\(a\)取钱,建边dfs即可,注意图不连通。

点此查看代码
#include<bits/stdc++.h>
using namespace std;
#define rep(i,s,t,p) for(int i = s;i <= t;i += p)
#define drep(i,s,t,p) for(int i = s;i >= t;i -= p)
#ifdef LOCAL
    auto I = freopen("in.in","r",stdin),O = freopen("out.out","w",stdout);
#else
    auto I = stdin,O = stdout;
#endif
using ll = long long;using ull = unsigned long long;
using db = long double;using ldb = long double;
const int N = 3e4 + 10;
#define eb emplace_back
vector<int> e[N],ans;
bitset<N> vis;
int n,m,d[N];
void dfs(int x,int fa){
    vis[x] = true;
    for(auto y:e[x]){
        if(vis[y]) continue;
        dfs(y,x);
    }
    ans.emplace_back(x);
}
signed main(){
    cin.tie(nullptr)->sync_with_stdio(false);
    cin>>n>>m;
    rep(i,1,m,1){int x,y;cin>>x>>y;e[x].eb(y);}
    rep(i,1,n,1) if(!vis[i]) dfs(i,0);
    for(auto i:ans) cout<<i<<' ';
}

[BJWC2010] 严格次小生成树

一个结论:次小生成树与最小生成树只会有一条边不同。

先将最小生成树求出来,然后考虑所有的非树边,发现这条边\((u,v,w)\)替换的必定是\(u\rightarrow LCA(u,v),v\rightarrow LCA(u,v)\)上的一条边\((u^\prime,v^\prime,w^\prime),w^\prime < w\)。

那么现在就是求\(u\rightarrow v\)的简单路径上所有的边中,\(w\)的前驱。

不会倍增,只会树剖+线段树,考虑将所有树边排序,每次查询非树边时,将所有权值小于它的假如,然后求链上最大值即可,但是边权不好做,考虑边权转点权即可,时间复杂度\(O(m\log^2 n)\),但是由于树剖常数小,跑的和倍增差不多,而且空间比倍增优秀。

点此查看代码
#include<bits/stdc++.h>
using namespace std;
#define rep(i,s,t,p) for(int i = s;i <= t;i += p)
#define drep(i,s,t,p) for(int i = s;i >= t;i -= p)
#ifdef LOCAL
    auto I = freopen("in.in","r",stdin),O = freopen("out.out","w",stdout);
#else
    auto I = stdin,O = stdout;
#endif
using ll = long long;using ull = unsigned long long;
using db = long double;using ldb = long double;
#define eb emplace_back
const int N = 1e5 + 10,M = 3e5 + 10;
vector<pair<int,int> > e[N];
int n,m;struct node{int x,y,z;}edge[M];
bitset<M> unused;
int dist[N],top[N],siz[N],son[N],dfn[N],rdfn[N],tim,dep[N],fa[N];
struct DSU{
    vector<int> fa;
    void init(int n){fa.resize(n+1);rep(i,1,n,1) fa[i] = i;}
    int get_fa(int x){while(x ^ fa[x]) x = fa[x] = fa[fa[x]];return x;}
    bool Merge(int x,int y){
        x = get_fa(x),y = get_fa(y);
        if(x == y) return false;
        return fa[x] = y,true;
    }
}D;
struct Segment_Tree{
    struct segment_tree{
        int l,r,val;
        #define l(x) tree[x].l
        #define r(x) tree[x].r
        #define val(x) tree[x].val
    }tree[N<<2];
    void B(int k = 1,int l = 1,int r = n){
        l(k) = l,r(k) = r;
        if(l == r) return;
        int mid = (l + r) >> 1;
        B(k<<1,l,mid);B(k<<1|1,mid+1,r);
    }
    void upd(int k,int pos,int val){
        if(l(k) == r(k)) return val(k) = val,void();
        int mid = (l(k) + r(k)) >> 1;
        pos <= mid?upd(k<<1,pos,val):upd(k<<1|1,pos,val);
        val(k) = max(val(k<<1),val(k<<1|1));
    }
    int qry(int k,int l,int r){
        if(l <= l(k) && r(k) <= r) return val(k);
        int mid = (l(k) + r(k)) >> 1,res = 0;
        if(l <= mid) res = max(res,qry(k<<1,l,r));
        if(r > mid) res = max(res,qry(k<<1|1,l,r));
        return res;
    }
}T;
void dfs1(int x){
    siz[x] = 1,dep[x] = dep[fa[x]] + 1;
    for(auto [y,w]:e[x]){
        if(y == fa[x]) continue;
        fa[y] = x;dfs1(y);dist[y] = w;
        siz[x] += siz[y];
        if(siz[son[x]] < siz[y]) son[x] = y;
    }
}
void dfs2(int x,int t){
    top[x] = t;rdfn[dfn[x] = ++tim] = x;
    if(son[x]) dfs2(son[x],t);else return;
    for(auto [y,w]:e[x]){
        if(y == fa[x] || y == son[x]) continue;
        dfs2(y,y);
    }
}
int query(int x,int y){
    int fx = top[x],fy = top[y];
    int res = 0;
    while(fx ^ fy){
        if(dep[fx] < dep[fy]) swap(x,y),swap(fx,fy);
        res = max(res,T.qry(1,dfn[fx],dfn[x]));
        x = fa[fx];fx = top[x];
    }
    if(dep[x] > dep[y]) swap(x,y);
    if(x == y) return res;
    res = max(res,T.qry(1,dfn[x]+1,dfn[y]));
    return res;
}
signed main(){
    cin.tie(nullptr)->sync_with_stdio(false);
    cin>>n>>m;rep(i,1,m,1) cin>>edge[i].x>>edge[i].y>>edge[i].z;
    D.init(n);
    sort(edge+1,edge+1+m,[](node x,node y){return x.z < y.z;});
    ll total = 0;
    rep(i,1,m,1){
        auto [x,y,w] = edge[i];
        auto flag = D.Merge(x,y);
        if(!flag){unused[i] = true;continue;}
        e[x].eb(y,w);e[y].eb(x,w);total += w;
    }
    dfs1(1);dfs2(1,1);T.B();
    int ans = 1e9;
    int now = 1;
    rep(i,1,m,1){
        auto [x,y,w] = edge[i];
        while(now <= m && edge[now].z < w){
            if(unused[now]){now++;continue;}
            auto [x,y,w] = edge[now];
            if(fa[x] == y) T.upd(1,dfn[x],edge[now].z);
            if(fa[y] == x) T.upd(1,dfn[y],edge[now].z);
            now++;
        }
        if(!unused[i]) continue;
        if(x == y) continue;
        int res = w-query(x,y);
        ans = min(ans,res);
    }
    cout<<total + ans<<'\n';
}

跳楼机

同余最短路板子,当个学习笔记写。

当出现形如

  1. 给定 \(n\) 个整数,求这 \(n\) 个整数能拼凑出多少的其他整数 (\(n\) 个整数可以重复取)。
  2. 给定 \(n\) 个整数,求这 \(n\) 个整数不能拼凑出的最小(最大)的整数。
  3. 至少要拼几次才能拼出模 \(K\) 余 \(p\) 的数

等问题时可以使用同余最短路的方法。

既然带了最短路,那么就类比最短路。同余最短路中的转移通常是\(f(i+y)=f(i)+y\),类比\(dist_v=dist_u+e(u,v)\)。

考虑这道题,设\(d_i\)表示通过\(2,3\)操作,满足\(p\bmod x=i\)的最小的\(p\),那么就有\(f((i+y)\bmod x)=f(i)+y,f((i+z)\bmod x)=f(i)+z\)。实质上就是最短路中的加边操作,接下来只需要求出\(d_0,d_1,\ldots,d_{x-1}\)即可,答案为\(\sum\limits_{i=0}^{x-1}[h\ge d_i](\frac{h-d_i}{x}+1)\)。但是由于本题\(h\le 2^{63}-1\),求解时\(d\)的初值至少应为\(2^63>LLONG\_MAX\),所以可以将\(h\leftarrow h-1\),将初始楼层设为\(0\)即可。

点此查看代码
#include<bits/stdc++.h>
using namespace std;
#define rep(i,s,t,p) for(int i = s;i <= t;i += p)
#define drep(i,s,t,p) for(int i = s;i >= t;i -= p)
#ifdef LOCAL
    auto I = freopen("in.in","r",stdin),O = freopen("out.out","w",stdout);
#else
    auto I = stdin,O = stdout;
#endif
using ll = long long;using ull = unsigned long long;
using db = long double;using ldb = long double;
#define int ull
const int N = 1e5 + 10;
int h,x,y,z,dist[N];
vector<pair<int,int> > e[N];
bitset<N> vis;
void dijkstra(){
    memset(dist,0x3f,sizeof dist);
    priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > > q;
    q.emplace(0,0);dist[0] = 0;
    while(q.size()){
        int x = q.top().second;q.pop();
        if(vis[x]) continue;
        vis[x] = true;
        for(auto [y,w]:e[x]){
            if(dist[y] > dist[x] + w){
                dist[y] = dist[x] + w;
                q.emplace(dist[y],y);
            }
        }
    }
}
signed main(){
    cin.tie(nullptr)->sync_with_stdio(false);
    cin>>h>>x>>y>>z;h--;
    rep(i,0,x-1,1){
        e[i].emplace_back((i+y)%x,y);
        e[i].emplace_back((i+z)%x,z);
    }
    dijkstra();
    int ans = 0;
    rep(i,0,x-1,1) if(h >= dist[i]) ans += (h-dist[i])/x+1;
    cout<<ans<<'\n';
}

[国家集训队] 墨墨的等式

显然答案具有可差分性,考虑求出\([0,l),[0,r]\)中的可以凑出的值作差即可,处理方法同上。

点此查看代码
#include<bits/stdc++.h>
using namespace std;
#define rep(i,s,t,p) for(int i = s;i <= t;i += p)
#define drep(i,s,t,p) for(int i = s;i >= t;i -= p)
#ifdef LOCAL
    auto I = freopen("in.in","r",stdin),O = freopen("out.out","w",stdout);
#else
    auto I = stdin,O = stdout;
#endif
using ll = long long;using ull = unsigned long long;
using db = long double;using ldb = long double;
const int N = 5e5 + 10;
int n,x;bitset<N> vis;
vector<pair<int,int> > e[N];
ll l,r,dist[N];
vector<int> num;
void dijkstra(int s){
    memset(dist,0x3f,sizeof dist);
    priority_queue<pair<ll,int>,vector<pair<ll,int> >,greater<pair<ll,int> > > q;
    q.emplace(0,0);dist[0] = 0;
    while(q.size()){
        int x = q.top().second;q.pop();
        if(vis[x]) continue;
        vis[x] = true;
        for(auto [y,w]:e[x]){
            if(dist[y] > dist[x] + w){
                dist[y] = dist[x] + w;
                q.emplace(dist[y],y);
            }
        }
    }
}
signed main(){
    cin.tie(nullptr)->sync_with_stdio(false);
    cin>>n>>l>>r>>x;l--;
    rep(i,2,n,1){int x;cin>>x;num.emplace_back(x);}
    rep(i,0,x-1,1) for(auto j:num) e[i].emplace_back((i+j)%x,j);
    dijkstra(0);
    ll ans = 0;
    rep(i,0,x-1,1) if(l >= dist[i]) ans -= (l-dist[i])/x+1;
    rep(i,0,x-1,1) if(r >= dist[i]) ans += (r-dist[i])/x+1;
    cout<<ans<<'\n';
}

Fairy

【模板】线段树分治

线段树分治太naive了,考虑换个优秀的做法。

显然答案为奇环交,考虑如何求这个。考虑跑一遍dfs,求出一颗生成树。

对于一条非树边,显然会形成一个环,称这个环上的树边被其覆盖。

如果一条非树边和树边形成一个有且仅有这一条非树边的奇环,那么称这条非树边为坏边。

显然答案边被所有坏边覆盖,且不被好边覆盖。

然后直接树上差分就可以了,时间复杂度\(O(n)\)。

点此查看代码
#include<bits/stdc++.h>
using namespace std;
#define rep(i,s,t,p) for(int i = s;i <= t;i += p)
#define drep(i,s,t,p) for(int i = s;i >= t;i -= p)
#ifdef LOCAL
    auto I = freopen("in.in","r",stdin),O = freopen("out.out","w",stdout);
#else
    auto I = stdin,O = stdout;
#endif
using ll = long long;using ull = unsigned long long;
using db = long double;using ldb = long double;
const int N = 1e4 + 10;
#define eb emplace_back
vector<int> e[N],id[N],ans;
bitset<N> vis;
bool dis[N],ok[N];
int s[N],ct,eid,n,m;
void dfs(int x){
    vis[x] = true;
    rep(i,0,(int)e[x].size()-1,1){
        int y = e[x][i];
        if(!vis[y]){
            dis[y] = dis[x]^1;
            ok[id[x][i]] = 1;dfs(y);
        }
        else if(!ok[id[x][i]]){
            ok[id[x][i]] = 1;
            if(dis[y] == dis[x]){
                ct++;s[x]++;s[y]--;
                eid = id[x][i];
            }
            else s[x]--,s[y]++;
        }
    }
}
void dfs1(int x){
    vis[x] = true;
    rep(i,0,(int)e[x].size() - 1,1){
        int y = e[x][i];
        if(!vis[y]){
            dfs1(y);
            if(s[y] == ct) ans.emplace_back(id[x][i]);
            s[x] += s[y];
        }
    }
}
signed main(){
    cin.tie(nullptr)->sync_with_stdio(false);
    cin>>n>>m;
    rep(i,1,m,1){
        int u,v;cin>>u>>v;
        e[u].eb(v);e[v].eb(u);
        id[u].eb(i);id[v].eb(i);
    }
    rep(i,1,n,1) if(!vis[i]) dfs(i);
    if(!ct){
        cout<<m<<'\n';
        rep(i,1,m,1) cout<<i<<' ';
        return 0;
    }
    vis.reset();
    rep(i,1,n,1) if(!vis[i]) dfs1(i);
    if(ct == 1) ans.emplace_back(eid);
    cout<<ans.size()<<'\n';
    sort(ans.begin(),ans.end());
    for(auto i:ans) cout<<i<<' ';
}

[中山市选] 杀人游戏

一眼下去答案为\(\frac{n-缩点后入度为0的点的个数}{n}\)。然后喜提30pts。

考虑哪里有问题呢?贪心的想,我们可以将强连通分量中个数为\(1\)且所连的点的入度都\(>1\)的点放在最后一个询问,如果其它点都确定了,那么这个点显然不用问,答案为\(\frac{n-缩点后入度为0的点的个数}{n}\)。

本题卡精度,输出答案时应加上eps。

点此查看代码
#include<bits/stdc++.h>
using namespace std;
#define rep(i,s,t,p) for(int i = s;i <= t;i += p)
#define drep(i,s,t,p) for(int i = s;i >= t;i -= p)
#ifdef LOCAL
    auto I = freopen("in.in","r",stdin),O = freopen("out.out","w",stdout);
#else
    auto I = stdin,O = stdout;
#endif
using ll = long long;using ull = unsigned long long;
using db = long double;using ldb = long double;
const int N = 1e5 + 10;
#define eb emplace_back
int n,m;vector<int> e[N];
int dfn[N],low[N],tim,sta[N],top;
int tot = 0,g[N],u[N],v[N],d[N],siz[N],ct[N],out[N];
bitset<N> insta;set<int> pd[N];
void Tarjan(int x){
    dfn[x] = low[x] = ++tim;
    sta[++top] = x;insta[x] = true;
    for(auto y:e[x]){
        if(!dfn[y]) Tarjan(y),low[x] = min(low[x],low[y]);
        else if(insta[y]) low[x] = min(low[x],dfn[y]);
    }
    if(dfn[x] == low[x]){
        int y;tot++;
        do{
            y = sta[top--];
            g[y] = tot;
            insta[y] = false;
            siz[tot]++;
        }while(y != x);
    }
}
vector<pair<int,int> > E;
signed main(){
    cin.tie(nullptr)->sync_with_stdio(false);
    cin>>n>>m;
    rep(i,1,m,1){
        int u,v;cin>>u>>v;
        if(pd[u].count(v)) continue;
        pd[u].insert(v);
        e[u].eb(v);E.eb(u,v);
    }
    rep(i,1,n,1) pd[i].clear();
    rep(i,1,n,1) if(!dfn[i]) Tarjan(i);
    for(auto [x,y]:E){
        if(g[x] == g[y]) continue;
        if(pd[g[x]].count(g[y])) continue;
        pd[g[x]].insert(g[y]);
        out[g[x]]++;
        d[g[y]]++;
    }
    ldb ans = 0;
    for(auto [x,y]:E){
        if(g[x] == g[y]) continue;
        if(d[g[y]] >= 2) ct[g[x]]++;
    }
    rep(i,1,tot,1){
        if(!d[i] && ct[i] == out[i] && siz[i] == 1){ans -= 1;break;}
    }
    rep(i,1,tot,1){if(!d[i])ans += 1;}
    cout<<fixed<<setprecision(6);
    cout<<1.0*(n-ans)/n+1e-9<<'\n';
}

[NOI2018] 归程

模板:kruskal重构树。

就是用kruskal时,将两个点中间创建一个虚点,点权记为边权。

有如下性质:

  1. 两个点之间的所有简单路径上最大边权的最小值 = 最小生成树上两个点之间的简单路径上的最大值 = Kruskal 重构树上两点之间的 LCA 的权值。
  2. 如果以最小生成树的方式建树,那么建出的树是大根堆的形式。

本题则求最大生成树,就利用小根堆的性质,建出kruskal重构树后,每个点维护子树内\(1\)到子树内的点的最小值。

由于不会倍增,考虑树剖暴力跳,如果发现链顶无法达到,那么就在这条重链上二分即可。注意树剖清空,不然会导致奇怪错误。

时间复杂度\(O(m\log m + q\log n)\)。

点此查看代码
#include<bits/stdc++.h>
using namespace std;
#define rep(i,s,t,p) for(int i = s;i <= t;i += p)
#define drep(i,s,t,p) for(int i = s;i >= t;i -= p)
#ifdef LOCAL
    auto I = freopen("in.in","r",stdin),O = freopen("out.out","w",stdout);
#else
    auto I = stdin,O = stdout;
#endif
using ll = long long;using ull = unsigned long long;
using db = long double;using ldb = long double;
#define int ll
#define eb emplace_back
const int N = 4e5 + 10;
vector<pair<int,int> > e[N],t[N];
struct Edge{int x,y,l,a;}edge[N];
int n,m,vtot,fa[N],siz[N],son[N],top[N],dfn[N],rdfn[N],tim,dep[N],dis[N],lastans;
int v,p,q,k,s;
ll dist[N];
bitset<N> vis;
void dijkstra(int s = 1){
    memset(dist,0x3f,sizeof dist);vis.reset();
    priority_queue<pair<ll,int>,vector<pair<ll,int> >,greater<pair<ll,int> > > q;
    dist[s] = 0;q.emplace(0,s);
    while(q.size()){
        int x = q.top().second;q.pop();
        if(vis[x]) continue;
        vis[x] = true;
        for(auto [y,w]:e[x]){
            if(dist[y] > dist[x] + w){
                dist[y] = dist[x] + w;
                q.emplace(dist[y],y);
            }
        }
    }
}
struct DSU{
    vector<int> fa;
    void init(int n){fa.resize(n+1); rep(i,1,n,1) fa[i] = i;}
    int get_fa(int x){while(x ^ fa[x]) x = fa[x] = fa[fa[x]];return x;}
    bool Merge(int x,int y){
        x = get_fa(x),y = get_fa(y);
        if(x == y) return false;
        if(x > y) swap(x,y);
        return fa[x] = y,true;
    }
}D;
void dfs1(int x){
    dep[x] = dep[fa[x]] + 1;siz[x] = 1;
    for(auto [y,w]:t[x]){
        if(y == fa[x]) continue;
        dis[y] = w;fa[y] = x;dfs1(y);
        siz[x] += siz[y];
        dist[x] = min(dist[x],dist[y]);
        if(siz[son[x]] < siz[y]) son[x] = y;
    }
}
void dfs2(int x,int tp){
    top[x] = tp;rdfn[dfn[x] = ++tim] = x;
    if(son[x]) dfs2(son[x],tp);else return;
    for(auto [y,w]:t[x]){
        if(y == fa[x] || y == son[x]) continue;
        dfs2(y,y);
    }
}
void init(){
    rep(i,1,n*2+10,1) e[i].clear(),t[i].clear();
    memset(fa,0,sizeof fa);memset(siz,0,sizeof siz);
    memset(dfn,0,sizeof dfn);memset(rdfn,0,sizeof rdfn);
    memset(dep,0,sizeof dep);memset(top,0,sizeof top);
    memset(son,0,sizeof son);
    sort(edge+1,edge+1+m,[](Edge x,Edge y){return x.a > y.a;});
    D.init(n*2+10);
    rep(i,1,m,1){
        auto [x,y,l,a] = edge[i];
        e[x].eb(y,l);e[y].eb(x,l);
        if(D.get_fa(x) == D.get_fa(y)) continue;
        vtot++;
        t[vtot].eb(D.get_fa(x),a);t[vtot].eb(D.get_fa(y),a);
        t[D.get_fa(x)].eb(vtot,a);t[D.get_fa(y)].eb(vtot,a);
        D.fa[D.get_fa(x)] = vtot;D.fa[D.get_fa(y)] = vtot;
    }
    dijkstra();dfs1(vtot);tim = 0;dfs2(vtot,vtot);
}
int get(int v,int p){
    int x = v,fx = top[x];
    while(x && dis[fx] > p) x = fa[fx],fx = top[x];
    int l = dfn[fx],r = dfn[x],res = 0;
    while(l <= r){
        int mid = (l + r) >> 1;
        if(dis[rdfn[mid]] <= p) res = rdfn[mid],l = mid + 1;
        else r = mid - 1;
    }
    if(!res || res == 1) return 0;
    return dist[res];
}
signed main(){
    cin.tie(nullptr)->sync_with_stdio(false);
int T;cin>>T;
rep(Test,1,T,1){
    cin>>n>>m;vtot = n;lastans = 0;
    rep(i,1,m,1)cin>>edge[i].x>>edge[i].y>>edge[i].l>>edge[i].a;
    init();cin>>q>>k>>s;
    rep(test,1,q,1){
        cin>>v>>p;
        v = (v+k*lastans - 1)%n+1;p = (p+k*lastans)%(s+1);
        cout<<(lastans = get(v,p))<<'\n';
    }
}
}

Xor-MST

考虑Boruvka算法,那么就只需要考虑如果对于每个点,快速求出不在其连通块中最小连边。

求异或最小值,考虑01-trie,建一个全局trie,然后对于每个连通块,维护每个连通块的trie,然后记录每个节点的size,对于每个点,在全局trie中查找,如果减去其所在连通块中的size仍有,正常跑就行,合并两个连通块的时候,启发式合并它们的trie即可。

时间复杂度\(O(n\log n\log V)\),空间复杂度\(O(n\log V)\)。

点此查看代码
#include<bits/stdc++.h>
#include<bits/extc++.h>
using namespace __gnu_pbds;
using namespace std;
#define rep(i,s,t,p) for(int i = s;i <= t;i += p)
#define drep(i,s,t,p) for(int i = s;i >= t;i -= p)
#ifdef LOCAL
    auto I = freopen("in.in","r",stdin),O = freopen("out.out","w",stdout);
#else
    auto I = stdin,O = stdout;
#endif
using ll = long long;using ull = unsigned long long;
using db = long double;using ldb = long double;
const int N = 2e5 + 10;
const ll inf = 1e15;
int n,a[N],rt[N],to[N];
ll mn[N];
struct Trie{
    int tree[N*60][2],siz[N*60],tot,ed[N*60];
    void insert(int &x,int dep,int w,int id){
        if(!x) x = ++tot;
        if(dep < 0) return ed[x] = id,siz[x] = 1,void();
        int c = w>>dep&1;
        insert(tree[x][c],dep-1,w,id);siz[x]++;
    }
    int Merge(int x,int y){
        if(!x || !y) return x|y;
        tree[x][0] = Merge(tree[x][0],tree[y][0]);
        tree[x][1] = Merge(tree[x][1],tree[y][1]);
        siz[x] = siz[tree[x][0]] + siz[tree[x][1]];
        ed[x] = ed[y];
        return x;
    }
    pair<int,int> get(int x,int pre,int w){
        int ans = 0;
        drep(i,30,0,1){
            int c = w>>i&1;
            if(tree[x][c] && siz[tree[x][c]] - siz[tree[pre][c]] > 0) 
                x = tree[x][c],pre = tree[pre][c];
            else ans |= 1<<i,x = tree[x][c^1],pre = tree[pre][c^1];
        }
        return make_pair(ans,ed[x]);
    }
}T;
struct DSU{
    vector<int> fa,siz;int tot;
    void init(int n){tot = n;fa.resize(n+1),siz.resize(n+1,1);rep(i,1,n,1) fa[i] = i;}
    int get_fa(int x){while(x ^ fa[x]) x = fa[x] = fa[fa[x]];return x;}
    pair<int,int> Merge(int x,int y){
        x = get_fa(x),y = get_fa(y);
        if(x == y) return make_pair(0,0);
        tot--;
        if(siz[x] > siz[y]) swap(x,y);
        fa[x] = y;siz[y] += siz[x];
        return make_pair(x,y);
    }
}D;
signed main(){
    cin.tie(nullptr)->sync_with_stdio(false);
    vector<int> num;
    cin>>n;rep(i,1,n,1) cin>>a[i],num.emplace_back(a[i]);
    sort(a+1,a+1+n);n = unique(a+1,a+1+n) - a - 1;
    rep(i,1,n,1) T.insert(rt[0],30,a[i],i),T.insert(rt[i],30,a[i],i);
    D.init(n);bitset<N> vis;
    ll ans = 0;int tot = 0;
    while(D.tot != 1){
        tot++;
        rep(i,1,n,1) mn[i] = inf;
        rep(i,1,n,1){
            auto res = T.get(rt[0],rt[D.get_fa(i)],a[i]);
            if(res.first < mn[D.get_fa(i)]) 
                mn[D.get_fa(i)] = res.first,to[D.get_fa(i)] = D.get_fa(res.second);
        }
        bool flag = true;
        rep(i,1,n,1){
            if(mn[i] == inf) continue;
            flag &= false;
            auto res = D.Merge(i,to[i]);
            if(res.first) T.Merge(rt[res.second],rt[res.first]),ans += mn[i];
        }
    }
    cout<<ans<<'\n';
}

\(to\;be\;continued\)

标签:图论,专题,dist,fa,省选,rep,long,int,using
From: https://www.cnblogs.com/hzoi-Cu/p/18624994

相关文章

  • 框架专题:反射
    1.什么是反射?简单来说,反射是一种程序自省的能力,即在程序运行时动态地获取其结构信息或操作其行为。这包括类、方法、属性等元信息。反射的核心在于让代码变得更加动态化,从而突破静态语言的限制。以Java为例,反射允许你:动态获取一个类的名称、字段、方法、构造函数等信息;创建......
  • 【递归,搜索与回溯算法 & 综合练习】深入理解暴搜决策树:递归,搜索与回溯算法综合小专题(
       优美的排列  题目解析     算法原理     解法 :暴搜     决策树   红色剪枝:用于剪去该节点的值在对应分支中,已经被使用的情况,可以定义一个check[]紫色剪枝:perm[i]不能够被i整除,i不能够被perm[i]整除,此时分......
  • 2025省选集训Day1 略解
    前言且视他人之疑目如盏盏鬼火,大胆地去走你的夜路。先做的前天的DP套DP,可能浪费了一个上午在麻将这个题上,感觉今天的题做不完了。A略BTZL说很简单,我信了,然后想了40多分钟都没搞明白简单在哪里。然后他又说随便找规律,然后完全不知道怎么找。简称太菜了。我们发现,......
  • 编写程序,求字符串长度(指针专题)。编写一函数len,求一个字符串的长度,注意该长度不计空格
    #include<stdio.h>intlen(char*sp){        intcount=0;        for(inti=0;;i++)        {                  if(*(sp+i)=='')                           continue;  ......
  • 编写程序,利用指针实现排序(指针专题)。将输入的四个整数按由大到小的顺序输出。 已定义
    #include<stdio.h>voidswap(int*pa,int*pb){   intt;   t=*pa;*pa=*pb;*pb=t;}intmain(){   intarr[4]={0};   inti,j,n=4;   for(i=0;i<4;i++)   {       scanf("%d",&arr[i]);   }   ......
  • 图论 网络流总结
    图论|网络流总结NOI2018归程题目描述本题的故事发生在魔力之都,在这里我们将为你介绍一些必要的设定。魔力之都可以抽象成一个\(n\)个节点、\(m\)条边的无向连通图(节点的编号从\(1\)至\(n\))。我们依次用\(l,a\)描述一条边的长度、海拔。作为季风气候的代表城市,魔力......
  • 音视频入门基础:MPEG2-TS专题(21)——FFmpeg源码中,获取TS流的视频信息的实现
    一、引言通过FFmpeg命令可以获取到TS文件/TS流的视频压缩编码格式、色彩格式(像素格式)、分辨率、帧率信息:./ffmpeg-iXXX.ts本文以H.264为例讲述FFmpeg到底是从哪个地方获取到这些视频信息的。 二、视频压缩编码格式FFmpeg获取TS文件/TS流的视频压缩编码格式,是从PM......
  • SD模块-专题方案-销项税MWST税率确定逻辑(事务码VK11 & 后台表A002 & KNOH & KONP)
    销项税税率确定逻辑如下:1处,显示客户税分类来源于BP客户主数据-销售与分销视图2处,显示物料税分类来源于物料主数据销售视图23处,显示VK13维护的销项税税率17%4处,显示VA43销售合同中最终进行的销项税税率确定的17%税率事务码VK11维护销项税税率还是需要使用事务码FTXP查看......
  • 【递归,搜索与回溯算法 & 综合练习】深入理解暴搜决策树:递归,搜索与回溯算法综合小专题(
       找出所有子集的异或总和再求和  题目解析     算法原理     解法     决策树   这种决策使得每一次递归都是有效的递归,每一个节点都是最终的结果,所以这棵决策树是不用剪枝的,也没有递归出口的;    注意   ......
  • 2.图论
    省选图论专题开题顺序:\(GAJDCFO\)\(A\)luoguP4151[WC2011]最大XOR和路径题解\(B\)CF19EFairy\(C\)CF412DGivingAwards建出反图后拓扑排序无法处理欠钱关系中存在环的情况,但可以借鉴其思路。仍考虑自下而上叫人,在叫完所有子节点后再加入答案即可。点击查......