线段树分治结构
线段树分治结构
基本知识:
应用点: 当有些东西一会出现,一会又不出现时,可以使用线段树分治
方式: 维护某一个东西出现的时间段,并在线段树上打上标记,并dfs
遇到标记后,就相当于加入了这个物品。当dfs到叶子节点后,就可以得到叶子节点所代表的时间的性质
dfs返回时,若经过遇到标记的地方,需要撤回这个标记,相当于这个物品不再在答案统计内
正因如此,若是要用某个结构来维护性质,要求能够撤回
这些常见的结构有:线性运算/并查集(例题1)/dp方程式(例题2)
例题1: 模板(基础题)
题目链接:https://www.luogu.com.cn/problem/P5787
AC记录:https://www.luogu.com.cn/record/116043014
思想: 并查集维护同色关系,可以拆点或带权并查集,利用栈来维护撤回操作
代码:
#include<bits/stdc++.h>
using namespace std;
int n,m,k;
struct node{
int l,r,x,y;
}line[200005];
struct nod{
int ll;
int rr;
}tree[800045];
int d[200005];
stack<pair<int,bool>>s;
vector<pair<int,int>>flag[800045];
void build(int rt,int l,int r){
tree[rt].ll=l;
tree[rt].rr=r;
if(l==r){
return;
}
int mid=(l+r)/2;
build(rt*2,l,mid);
build(rt*2+1,mid+1,r);
}
void updata(int rt,int cl,int cr,int flag1,int flag2){
int le=tree[rt].ll;
int ri=tree[rt].rr;
if(le>cr||ri<cl){
return;
}
if(le>=cl&&ri<=cr){
flag[rt].push_back(make_pair(flag1,flag2));
return;
}
updata(rt*2,cl,cr,flag1,flag2);
updata(rt*2+1,cl,cr,flag1,flag2);
}
//-----------------------------------------打标记线段树
int fa[400005];
int fid(int x){
if(x==fa[x]){
return x;
}
else{
return fid(fa[x]);
}
}
void he(int x, int y){
x=fid(x);
y=fid(y);
if(x==y){
return;
}
if(d[x]>d[y]) swap(x, y);
s.push(make_pair(x,d[x]==d[y]));
fa[x]=y;
if(d[x]==d[y]){
d[y]++;
}
}
void dfs(int rt,int right){
int l=tree[rt].ll;
int r=tree[rt].rr;
int o=s.size();
for(int i=0;i<flag[rt].size();i++){
int a=flag[rt][i].first;
int b=flag[rt][i].second;
if(fid(a)==fid(b)){
for(int j=l;j<=r;j++){
cout<<"No"<<endl;
}
right=0;
break;
}
else{
he(a,b+n);
he(b,a+n);
}
}//标记使用
if (right) {
if (l==r){
cout<<"Yes"<<endl;
}
else{
dfs(rt*2,right);
dfs(rt*2+1,right);
}
}
while (s.size() > o){
d[fa[s.top().first]]-=s.top().second;
fa[s.top().first]=s.top().first;
s.pop();
}
}
int main(){
ios::sync_with_stdio(false);
cin >> n >> m >> k;
build(1,0,k-1);
for(int i=1;i<=m;i++){
cin >> line[i].x>>line[i].y>>line[i].l>>line[i].r;
line[i].r--;
updata(1,line[i].l,line[i].r,line[i].x,line[i].y);
}
for(int i=1;i<=2*n;i++){
fa[i]=i;
d[i]=1;
}
dfs(1,1);
}
example 2
https://www.luogu.com.cn/problem/P5227
#include<bits/stdc++.h>
using namespace std;
struct li{
int l;
int r;
int id;
}line[200005];
int n,m;
//考虑从后到前加边是錯的
/*
md把这道题想难了,我以为是每一次操作有后续性
注意下去重问题!!!!!!!!!并不是说一个边只会删除一次,而剩下时间都是存在的!
注意下inc必需寫成局部變量!!!
*/
bool vis[400005] ;
vector<int>del[400005];
vector<int>sign[1600005];
struct tr{
int ll;
int rr;
}tree[1600005];
void build(int rt,int l,int r){
int mid=(l+r)>>1;
tree[rt].ll=l;
tree[rt].rr=r;
if(l==r){
return ;
}
build(rt*2,l,mid);
build(rt*2+1,mid+1,r);
}
void updata(int rt,int cl,int cr,int id){
int l=tree[rt].ll;
int r=tree[rt].rr;
// int mid=(l+r)/2;
if(l>cr||r<cl){
return;
}
if(cl<=l&&r<=cr){
sign[rt].push_back(id);
return;
}
updata(rt*2,cl,cr,id);
updata(rt*2+1,cl,cr,id);
}
bool ans[400005];
//------------------------------------
int fa[400005];
int sz[400005];
int fid(int x){
if(fa[x]==x){
return x;
}
else return fid(fa[x]);
}
struct nod{
int x;
int y;
int szx;
int szy;
};
stack<nod>st;
void he(int x,int y){
x=fid(x);
y=fid(y);
if(x==y){
return ;
}
st.push((nod){x,y,sz[x],sz[y]});
if(sz[x]<sz[y]){
fa[x]=y;
sz[y]+=sz[x];
}
else{
fa[y]=x;
sz[x]+=sz[y];
}
}
//------------------------------------
void dfs(int rt){
int inc=st.size();
for(int i=0;i<sign[rt].size();i++){
int now=sign[rt][i];
he(line[now].l,line[now].r);
}
if(tree[rt].ll==tree[rt].rr){
if(sz[fid(n)]==n){
ans[tree[rt].ll]=1;
}
else{
ans[tree[rt].ll]=0;
}
while(st.size()>inc){
nod t=st.top();
st.pop();
fa[t.x]=t.x;
fa[t.y]=t.y;
sz[t.x]=t.szx;
sz[t.y]=t.szy;
}
return;
}
dfs(rt*2);
dfs(rt*2+1);
while(st.size()>inc){
nod t=st.top();
st.pop();
fa[t.x]=t.x;
fa[t.y]=t.y;
sz[t.x]=t.szx;
sz[t.y]=t.szy;
}
}
int main(){
ios::sync_with_stdio(false);
cin >> n >> m;
for(int i=1;i<=m;i++){
cin >> line[i].l >> line[i].r;
line[i].id=i;
}
for(int i=1;i<=n;i++){
fa[i]=i;
sz[i]=1;
}
int k;
cin >> k;
build(1,1,k);
for(int i=1;i<=k;i++){
int szz;
cin >> szz;
for(int j=1;j<=szz;j++){
int a;
cin >> a;
// del[i].push_back(a);//i+1时加入了a这些边 ,如0时刻删除的一组在1时刻还存在
del[a].push_back(i);//某个边不能存在的时间
vis[a]=1;
}
}
for(int i=1;i<=m;i++){
if(vis[i]==0){//从未删除
updata(1,1,k,i);
}
else{
sort(del[i].begin(),del[i].end());
int lasforbid=0;
for(int j=0;j<del[i].size();j++){
int forbid=del[i][j];
if(lasforbid+1>forbid-1){//在2,3都被禁止时,到3时会满足此条件
lasforbid=forbid;
continue;
}
updata(1,lasforbid+1,forbid-1,i);
lasforbid=forbid;
}
if(lasforbid<k){
updata(1,lasforbid+1,k,i);
}
}
}
dfs(1);
for(int i=1;i<=k;i++){
if(ans[i]==1){
cout<<"Connected"<<endl;
}
else{
cout<<"Disconnected"<<endl;
}
}
}
例题3: dp(背包)(会了就掌握题)
题目链接:http://222.180.160.110:1024/problem/5503
也可以去:https://loj.ac/p/6515
思想: 物品我一个个加入,同理我也可以一个个撤回,其实还是枚举了物品的,不过是有规律的罢了,利用cnt来记录物品进入先后关系(从而好撤回)
代码:有点难写啊
#include<bits/stdc++.h>
using namespace std;
int t,m,mod;
const int M=50005;
int tmm[M];
int L[M],R[M],ans[M];
struct node{
int tim;
int w;
int v;
};
//----------------------pack
int dp[M][505];
int cnt=0;
void clear(){
for(int i=0;i<M;i++){
for(int j=0;j<500;j++){
dp[i][j]=-0x3f3f3f3f;
}
}
dp[0][0]=0;
}
void insert(pair<int,int>now){
int x=now.first%mod;
int y=now.second;
cnt++;
for(int i=0;i<mod;i++){
dp[cnt][i]=dp[cnt-1][i];
}
for(int i=0;i<mod;i++){
if(x<=i){
dp[cnt][i]=max(dp[cnt][i],dp[cnt-1][i-x]+y);
}
else{
dp[cnt][i]=max(dp[cnt][i],dp[cnt-1][i+mod-x]+y);
}
}
}
void recall(){
for(int i=0;i<500;i++){
dp[cnt][i]=-0x3f3f3f3f;
}
cnt--;
}
int query(int l,int r){
int ret=-1;
for(int i=l;i<=r;i++){
ret=max(ret,dp[cnt][i]);
}
return ret;
}
//---------------------tree
struct nod{
int ll;
int rr;
}tree[4*M];
vector<pair<int,int>>flag[4*M];
void build(int rt,int l,int r){
tree[rt].ll=l;
tree[rt].rr=r;
if(l==r){
return;
}
int mid=(l+r)/2;
build(rt*2,l,mid);
build(rt*2+1,mid+1,r);
}
void updata(int rt,int cl,int cr,int weight,int value){
int le=tree[rt].ll;
int ri=tree[rt].rr;
if(le>cr||ri<cl){
return;
}
if(le>=cl&&ri<=cr){
flag[rt].push_back(make_pair(weight,value));
return;
}
updata(rt*2,cl,cr,weight,value);
updata(rt*2+1,cl,cr,weight,value);
}
void solve(int rt){
int l=tree[rt].ll;
int r=tree[rt].rr;
int sz=flag[rt].size();
for(int i=0;i<sz;i++){
insert(flag[rt][i]);
}
if(l^r){
solve(rt*2);
solve(rt*2+1);
}
else if(L[l]^-1){
ans[l]=query(L[l],R[r]);
}
for(int i=0;i<sz;i++){
recall();
}
}
//---------------------------tree
int main(){
ios::sync_with_stdio(false);
cin >> t;
cin >> m >> mod;
clear();
memset(L,-1,sizeof(L));
build(1,1,m);
deque<node>q;
for(int i=1;i<=m;i++){
string op;
cin >> op;
int w,v;
if(op[0]=='I'&&op[1]=='F'){
cin >> w >> v;//前面放
node Q=(node){i,w,v};
q.push_front(Q);
}
else if(op[0]=='I'&&op[1]=='G'){
cin >> w >> v;//后面放
node Q=(node){i,w,v};
q.push_back(Q);
}
else if(op[0]=='D'&&op[1]=='F'){//删除前面
node Q=q.front();
q.pop_front();
updata(1,Q.tim,i-1,Q.w,Q.v);
}
else if(op[0]=='D'&&op[1]=='G'){//删除后面
node Q=q.back();
q.pop_back();
updata(1,Q.tim,i-1,Q.w,Q.v);//区间标记
}
else if(op[0]=='Q'&&op[1]=='U'){
int l,r;
cin >> l >> r;//w和mod在l-r区间内最强武力值
L[i]=l;
R[i]=r;
}
}
while(q.size()){
node Q=q.front();
q.pop_front();
updata(1,Q.tim,m,Q.w,Q.v);//永远不会消失的要注意添加
}
solve(1);
for(int i=1;i<=m;i++){
if(L[i]^-1){
cout<<ans[i]<<endl;
}
}
}
本文作者:linghusama
本文链接:https://www.cnblogs.com/linghusama/p/17560657.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步