ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

浅谈线段树分治

2022-06-02 22:03:31  阅读:204  来源: 互联网

标签:sz 浅谈 int 线段 分治 ff rec find first


思想

离线,把询问拆成若干个区间,放到线段树上,在线段树上递归处理,进一个区间就执行操作,出一个区间就撤销执行了的操作,需要支持可回退。

P5787 二分图 /【模板】线段树分治

以时间为轴建线段树,把所有的边都放到线段树对应的区间里,走到这个区间时就连边,可以使用扩展域并查集判二分图,并查集不写路径压缩可以支持回退,需要用按秩合并或势能保证复杂度。

Code
#include<bits stdc++.h="">
#define pii pair&lt;int,int&gt;
#define ls x&lt;&lt;1
#define rs x&lt;&lt;1|1
using namespace std;
const int _=4e5+5;
int n,m,k;
int ff[_],sz[_];
vector<pii> s[_&lt;&lt;1];
int find(int x){return ff[x]==x?x:find(ff[x]);}
void ins(int x,int l,int r,int xl,int xr,pii v){
    if(l &gt; xr || r &lt; xl) return;
    if(xl&lt;=l&amp;&amp;xr&gt;=r){
        s[x].push_back(v);
        return;
    }
    int mid=l+r&gt;&gt;1;
    if(xl&lt;=mid) ins(ls,l,mid,xl,xr,v);
    if(xr&gt;mid) ins(rs,mid+1,r,xl,xr,v);
}
void con(int x,int y,deque<pii> &amp;rec){
    int a=find(x),b=find(y);
    if(sz[a]&gt;sz[b]) swap(a,b);
    ff[a]=b,sz[b]+=sz[a];
    rec.push_front({a,b});
}
void solve(int x,int l,int r){
    deque<pii> rec;
    for(auto v:s[x]){
        int a=find(v.first),b=find(v.second);
        // cout&lt;&lt;a&lt;&lt;" "&lt;&lt;b&lt;&lt;endl;
        // cout&lt;&lt;"*: "&lt;&lt;v.first&lt;&lt;" "&lt;&lt;v.second&lt;&lt;endl;
        if(a==b){
            for(int i=l;i&lt;=r;++i) cout&lt;&lt;"No\n";
            for(auto v:rec) sz[v.second]-=sz[v.first],ff[v.first]=v.first;
            return;
        }
        con(v.first+n,v.second,rec);
        con(v.first,v.second+n,rec);
    }
    if(l==r) cout&lt;&lt;"Yes\n";
    else{
        int mid=l+r&gt;&gt;1;
        solve(ls,l,mid),solve(rs,mid+1,r);
    }
    for(auto v:rec) sz[v.second]-=sz[v.first],ff[v.first]=v.first;
}
int main(){
    ios::sync_with_stdio(0); cin.tie(0);
    cin&gt;&gt;n&gt;&gt;m&gt;&gt;k;
    for(int i=1;i&lt;=m;++i){
        int x,y,l,r; cin&gt;&gt;x&gt;&gt;y&gt;&gt;l&gt;&gt;r;
        ins(1,1,k,l+1,r,{x,y});
    }
    for(int i=1;i&lt;=2*n;++i) sz[ff[i]=i]=1;
    solve(1,1,k);
    return 0;
}
</pii></pii></pii></bits>

P5227 [AHOI2013]连通图

以集合为轴建线段树,不会被删除的边在预处理时直接加入,递归线段树区间依次加入会被删除的边(注意一下边加入的范围,在上一次之后到下一次之前),用并查集很好判断是否连通且可回退。

Code
#include<bits stdc++.h="">
#define ls x&lt;&lt;1
#define rs x&lt;&lt;1|1
#define pii pair&lt;int,int&gt;
using namespace std;
const int _=8e5+5;
int n,m,k;
int ff[_],sz[_],cnt;
vector<pii> s[_];
pii e[_];
bool vis[_];
map&lt;int,int&gt; pre;
int find(int x){return ff[x]==x?x:find(ff[x]);}
void solve(int x,int l,int r){
    deque<int> rec;
    for(auto v:s[x]){
        int a=find(v.first),b=find(v.second);
        if(a==b) continue;
        if(sz[a]&gt;sz[b]) swap(a,b);
        ++cnt,ff[a]=b,sz[b]+=a,rec.push_front(a);
    }
    if(l==r) cout&lt;&lt;(cnt==n-1?"Connected\n":"Disconnected\n");
    else{
        int mid=l+r&gt;&gt;1;
        solve(ls,l,mid),solve(rs,mid+1,r);
    }
    for(auto v:rec) sz[ff[v]]-=sz[v],ff[v]=v,--cnt;
}
void ins(int x,int l,int r,int xl,int xr,pii v){
    if(xl&gt;xr) return;
    if(xl&lt;=l&amp;&amp;xr&gt;=r) {s[x].push_back(v);return;}
    int mid=l+r&gt;&gt;1;
    if(xl&lt;=mid) ins(ls,l,mid,xl,xr,v);
    if(xr&gt;mid) ins(rs,mid+1,r,xl,xr,v);
}
int main(){
    ios::sync_with_stdio(0); cin.tie(0);
    cin&gt;&gt;n&gt;&gt;m;
    for(int i=1;i&lt;=n;++i) sz[ff[i]=i]=1;
    for(int i=1;i&lt;=m;++i) cin&gt;&gt;e[i].first&gt;&gt;e[i].second;
    cin&gt;&gt;k;
    for(int i=1;i&lt;=k;++i){
        int t; cin&gt;&gt;t;
        while(t--){
            int x; cin&gt;&gt;x;
            vis[x]=1;
            ins(1,1,k,pre[x]+1,i-1,e[x]);
            pre[x]=i;
        }
    }
    for(auto v:pre) if(v.second) ins(1,1,k,pre[v.first]+1,k,e[v.first]);
    for(int i=1;i&lt;=m;++i){
        if(!vis[i]){
            // cout&lt;&lt;"Pre: "&lt;&lt;i&lt;&lt;endl;
            int a=find(e[i].first),b=find(e[i].second);
            if(a==b) continue;
            if(sz[a]&gt;sz[b]) swap(a,b);
            ++cnt,ff[a]=b,sz[b]+=a;
        }
    }
    solve(1,1,k);
    return 0;
}
</int></pii></bits>

CF1681F Unique Occurrences

数数方式:每种颜色单独算贡献,选一个颜色,去除所有这种颜色的边,当前颜色贡献为所有连通块大小两两相乘再相加。

以颜色为轴,去除一种颜色的边等价于在处理这个颜色之前和处理完这个颜色之后才加入这条边,同理并查集维护连通块即可。

Code
#include<bits stdc++.h="">
#define pii pair&lt;int,int&gt;
#define ls x&lt;&lt;1
#define rs x&lt;&lt;1|1
using namespace std;
const int _=5e5+5;
int n,ff[_],sz[_];
long long ans;
vector<pii> E[_&lt;&lt;2],col[_];
int find(int x){return ff[x]==x?x:find(ff[x]);}
void insert(int x,int l,int r,int xl,int xr,pii v){
    if(xl&gt;xr) return;
    if(xl&lt;=l&amp;&amp;xr&gt;=r) {E[x].push_back(v);return;}
    int mid=l+r&gt;&gt;1;
    if(xl&lt;=mid) insert(ls,l,mid,xl,xr,v);
    if(xr&gt;mid) insert(rs,mid+1,r,xl,xr,v);
}
void calc(int x,int l,int r){
    deque<pii> rec;
    for(auto v:E[x]){
        int a=find(v.first),b=find(v.second);
        if(sz[a]&gt;sz[b]) swap(a,b);
        ff[a]=b,sz[b]+=sz[a],rec.push_front({a,b});
    }
    if(l==r) for(auto v:col[l]) ans+=1ll*sz[find(v.first)]*sz[find(v.second)];
    else calc(ls,l,l+r&gt;&gt;1),calc(rs,(l+r&gt;&gt;1)+1,r);
    for(auto v:rec) ff[v.first]=v.first,sz[v.second]-=sz[v.first];
}
int main(){
    ios::sync_with_stdio(0); cin.tie(0);
    cin&gt;&gt;n;
    for(int i=1;i&lt;=n;++i) sz[ff[i]=i]=1;
    for(int i=1;i&lt;n;++i){
        int x,y,z; cin&gt;&gt;x&gt;&gt;y&gt;&gt;z;
        col[z].push_back({x,y});
        insert(1,1,n,1,z-1,{x,y});
        insert(1,1,n,z+1,n,{x,y});
    }
    calc(1,1,n);
    cout&lt;&lt;ans;
    return 0;
}
</pii></pii></bits>

标签:sz,浅谈,int,线段,分治,ff,rec,find,first
来源: https://www.cnblogs.com/Quick-Kk/p/segdivide.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有