Codeforces707 D. Persistent Bookcase(可持久化线段树 / 离线建图dfs)

题意:

有n个书架,每个书架有m个格子,现在有四种操作,操作q次。
(1,i,j),在i号书架的第j个位置放书,如果有书则不放;
(2,i,j),在i号书架的第j个位置拿书,如果没书则不拿;
(3,i),将第i个书架有书的位置书拿走,将没书的位置放入书
(4,k),回溯到第k次修改的样子

每步操作之后输出当前书的总数

数据范围:n,m<=1e3,q<=1e5

解法:

解法1:
操作4需要可持久化,那么就用可持久化线段树做,
在叶子节点存一个m大小的bitset就行了(或者直接开一个m大小的数组),
维护以下区间和。
ps:
注意数组大小不要开错。
这种解法是在线的。

-----分割线-----

解法2:
将q个询问抽象为q个点
假设当前是第k个操作
对于前3个操作,将第k个操作连接在第k-1个操作后面,即建立一条k-1到k的有向边,
对于第4个操作,假设要回溯到第x个操作,那么将第k个操作连接在第x个操作后面,即建立一条x到k的有向边。

然后dfs(0),按顺序进行操作即可,回溯的时候撤销当前操作的影响
这样就可以巧妙的满足第4个操作的回溯了,妙啊!

ps:
这种做法需要离线
效率比第一种在线的可持久化线段树高,复杂度少一个log

code1:

#include<bits/stdc++.h>
using namespace std;
const int maxm=1e3+5;
const int N=1e5*log(1e5);
//
int lc[N],rc[N],sum[N];
bitset<1000+5>a[N];
int rt[100000+5],tot;
//
bitset<1000+5>full;
int n,m,q;
void update(int x,int y,int op,int l,int r,int last,int& k){
    k=++tot;
    //copy
    lc[k]=lc[last],rc[k]=rc[last];
    //
    if(l==r){
        a[k]=a[last];
        if(op==1){//置1
            a[k][y]=1;
        }else if(op==2){//置0
            a[k][y]=0;
        }else if(op==3){//取反
            a[k]^=full;
        }
        sum[k]=a[k].count();
        return ;
    }
    int mid=(l+r)/2;
    if(x<=mid)update(x,y,op,l,mid,lc[last],lc[k]);
    else update(x,y,op,mid+1,r,rc[last],rc[k]);
    sum[k]=sum[lc[k]]+sum[rc[k]];
}
signed main(){
    ios::sync_with_stdio(0);
    cin>>n>>m>>q;
    //init
    for(int i=1;i<=m;i++){
        full[i]=1;
    }
    //
    for(int i=1;i<=q;i++){
        int op;cin>>op;
        if(op==1){
            int x,y;cin>>x>>y;
            update(x,y,op,1,n,rt[i-1],rt[i]);
        }else if(op==2){
            int x,y;cin>>x>>y;
            update(x,y,op,1,n,rt[i-1],rt[i]);
        }else if(op==3){
            int x;cin>>x;
            update(x,x,op,1,n,rt[i-1],rt[i]);
        }else if(op==4){
            int x;cin>>x;
            rt[i]=rt[x];
        }
        cout<<sum[rt[i]]<<endl;
    }
    return 0;
}

code2:

#include<bits/stdc++.h>
using namespace std;
const int maxm=1e5+5;
struct Q{
    int op,x,y;
}q[maxm];
vector<int>g[maxm];
bitset<1000+5>a[1000+5];
bitset<1000+5>full;
int ans[maxm];
int cnt;
int n,m,k;
void dfs(int i){
    int flag=0;//修改标志,记录是否修改
    if(q[i].op==1){
        if(a[q[i].x][q[i].y]==0){
            a[q[i].x][q[i].y]=1;
            cnt++;
            flag=1;
        }
    }else if(q[i].op==2){
        if(a[q[i].x][q[i].y]==1){
            a[q[i].x][q[i].y]=0;
            cnt--;
            flag=1;
        }
    }else if(q[i].op==3){
        cnt-=a[q[i].x].count();
        a[q[i].x]^=full;
        cnt+=a[q[i].x].count();
    }
    ans[i]=cnt;
    for(int v:g[i]){
        dfs(v);
    }
    //回溯,撤回
    if(q[i].op==1){
        if(flag){
            a[q[i].x][q[i].y]=0;
            cnt--;
        }
    }else if(q[i].op==2){
        if(flag){
            a[q[i].x][q[i].y]=1;
            cnt++;
        }
    }else if(q[i].op==3){
        cnt-=a[q[i].x].count();
        a[q[i].x]^=full;
        cnt+=a[q[i].x].count();
    }
}
signed main(){
    ios::sync_with_stdio(0);
    cin>>n>>m>>k;
    //init
    for(int i=1;i<=m;i++){
        full[i]=1;
    }
    //
    q[0].op=-1;
    for(int i=1;i<=k;i++){
        cin>>q[i].op;
        if(q[i].op==1){
            cin>>q[i].x>>q[i].y;
            g[i-1].push_back(i);
        }else if(q[i].op==2){
            cin>>q[i].x>>q[i].y;
            g[i-1].push_back(i);
        }else if(q[i].op==3){
            cin>>q[i].x;
            g[i-1].push_back(i);
        }else if(q[i].op==4){
            cin>>q[i].x;
            g[q[i].x].push_back(i);
        }
    }
    dfs(0);
    for(int i=1;i<=k;i++){
        cout<<ans[i]<<endl;
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_44178736/article/details/107465416
今日推荐