Codeforces Round #368-D. Persistent Bookcase-(离线建树+暴力xjb搞)/(主席树在线乱搞)

http://codeforces.com/contest/707/problem/D


维护一个n*m的矩阵

题意就是4个操作,操作1,给a[i][j]置1

操作2,置零

操作3,第i行取反

操作4,回到做了k步之后的状态


---------------------------离线的做法:-------------------------------------------------------------------------------------------------------------------

把询问离线,按顺序建树,按照dfs序跑一遍就得到答案了】


例如操作i和i+1都是 非4的操作


那么i+1的父亲就是i咯,

如果i是4操作,那么i的父亲就会是k

那么按照这个顺序来跑一遍答案,就OK了,记得跑完x的操作后回溯的时候要撤掉操作(还原)


操作12都是单点,操作3用个数组记一下就好了。。。。都是o1的。。。。。


。比赛时sb了 怼了个bitset。。。估计会T  复杂度 O(q*n/64)

(居然没T,嘿嘿)

-------------------------------------------------------------------------------------------------------------------


--------------------------------------在线的做法-------------------------------------------------------------------------------------------------------------------

对于每一行书柜,我们用一个主席树维护m列的值,相当于n棵咯。

然后我们再开一个主席树,维护的是n行书柜对应主席树的根节点。

每次修改的时候,我们先在第n+1棵主席树找到 第x行对应的根节点,然后再在该树X上修改值。

每次新建2*logn个节点

空间复杂度q*logn

时间复杂度q*logn

....也很优秀,,不过还是离线了比较好搞。。。

确实大概类似与 一个可持久化数组套一个可持久化数组?


离线AC代码:

#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <queue>
#include <map>
#include <bitset>
#include <vector>
#include <iostream>
using namespace std;

const double pi=acos(-1.0);
double eps=0.000001;
typedef long long  ll;
struct node
{
    int kind,x,y;
};

vector<int >mp[100050];
int equ[100050];
node tt[100050];
int  fa[100050];
bitset<1005> sb[1005];
int ans[100050];
int num=0;
int n,m,q;
void deal(int x,int& vaild)
{
    int k=tt[x].kind;
    int i=tt[x].x;
    int j=tt[x].y;
    if (k==1)
    {
        if (!sb[i][j]) sb[i][j]=1,num++,vaild=1;
        else vaild=0;
    }
    if (k==2)
    {
        if ( sb[i][j]) sb[i][j]=0,num--,vaild=1;
        else vaild=0;
    }
    if (k==3)
    {
        int cun=sb[i].count();
        if (sb[i][1004]==1) cun-=(1005-m);
        num=num-cun+(m-cun);
        sb[i]=~sb[i];
    }
    if (k==4)
    {
    }
}
void redeal(int x,int& vaild)
{
    int k=tt[x].kind;
    int i=tt[x].x;
    int j=tt[x].y;
    if (k==1)
    {
        if (vaild) sb[i][j]=0,num--;
    }
    if (k==2)
    {
        if (vaild) sb[i][j]=1,num++;
    }
    if (k==3)
    {
        int cun=sb[i].count();
        if (sb[i][1004]==1) cun-=(1005-m);
        num=num-cun+(m-cun);
        sb[i]=~sb[i];
    }
    if (k==4)
    {
    }
}
void dfs(int x)
{
    int vaild;
    if (x)
    {
        deal(x,vaild);
        ans[x]=num;
    }
    for (int i=0; i<mp[x].size(); i++)
    {
        int v=mp[x][i];
        dfs(v);
    }
    if (x);
    redeal(x,vaild);
}
int main()
{

    for (int i=1; i<=10000; i++) equ[i]=i,fa[i]=i;
    fa[1]=0;
    cin>>n>>m>>q;
    num=0;

    for (int i=1; i<=q; i++)
    {
        scanf("%d",&tt[i].kind);
        if (tt[i].kind<=2)
            scanf("%d%d",&tt[i].x,&tt[i].y);
        else scanf("%d",&tt[i].x);
    }
    for (int i=2; i<=q; i++)
    {
        if (tt[i].kind!=4)
        {
           // fa[i]=equ[i-1];
            mp[i-1].push_back(i);
        }
        else
        {
           // equ[i]=tt[i].x+1;
           // fa[i]=fa[equ[i]];
            mp[tt[i].x].push_back(i);
        }
    }
    mp[0].push_back(1);
    dfs(0);
    for (int i=1; i<=q; i++)
    {
        printf("%d\n",ans[i]);
    }

    return 0;

}


主席树代码:

#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <vector>
#include <iostream>
using namespace std;
const int MAXNN=1e5 + 50;
const int MAXNnode=(2e5)*11 ;
#define w(i) T[(i)].w
#define ls(i) T[(i)].ls
#define rs(i) T[(i)].rs
int n;
struct node
{
    int ls,rs,w;
    node()
    {
        ls=rs=w=0;
    }
} T[MAXNnode];
int root[MAXNN],sz,m;
struct any
{
    int x,y,z;
    any() {}
    any(int a,int b,int c)
    {
        x=a,y=b,z=c;
    }
};
any getid(int i,int l,int r,int x)//取出在i时间的第x行对应的主席树的根节点
{
    if (l==r) return any(T[i].w,T[i].ls,T[i].rs);
    int m=(l+r)>>1;
    if (x<=m) return getid(ls(i),l,m,x);
    else return getid(rs(i),m+1,r,x);
}
void changeId(int &i,int l,int r,int x,any res)//修改在i时间的第x行对应的主席树子节点
{
    T[++sz]=T[i];
    i=sz;
    if (l==r)
    {
        T[i].w=res.x,T[i].ls=res.y,T[i].rs=res.z;
        return ;
    }
    int m=(l+r)>>1;
    if (x<=m) changeId(ls(i),l,m,x,res);
    else changeId(rs(i),m+1,r,x,res);
}
int getval(int i,int l,int r,int x) //取得某行对应的第x列的值
{
    if (r==l) return T[i].w;
    int m=(r+l)>>1;
    if   (x<=m) return getval(ls(i),l,m,x);
    else return getval(rs(i),m+1,r,x);
}
void  changeVal(int &i,int l,int r,int x)//修改某行对应的第x列的值
{
    T[++sz]=T[i];
    i=sz;
    if (r==l)
    {
        T[i].w^=1;
        return ;
    }
    int m=(r+l)>>1;
    if (x<=m) changeVal(ls(i),l,m,x);
    else changeVal (rs(i),m+1,r,x);
}

int ans[100005];
int main()
{
    //DO YOLO
    root[0]=0;
    sz=0;
    int n;
    int m,q;
    cin>>n>>m>>q;
    int kind,x,y;
    for (int i=1; i<=q; i++)
    {
        root[i]=root[i-1];
        ans[i]=ans[i-1];
        scanf("%d",&kind);
        if (kind==1)
        {
            scanf("%d%d",&x,&y);
            any res=getid(root[i],1,n,x);
            int val=getval(res.x,1,m,y);
            if ((val^res.y)==0)
            {
                int id=res.x;
                changeVal(id,1,m,y);
                res.z++;
                res.x=id;
                changeId(root[i],1,n,x,res);
                ans[i]++;
            }
        }
        else if (kind==2)
        {
            scanf("%d%d",&x,&y);
            any res=getid(root[i],1,n,x);
            int val=getval(res.x,1,m,y);
            if ((val^res.y)==1)
            {
                int id=res.x;
                changeVal(id,1,m,y);
                res.z--;
                res.x=id;
                changeId(root[i],1,n,x,res);
                ans[i]--;
            }
        }
        else if (kind==3)
        {
            scanf("%d",&x);
            any res=getid(root[i],1,n,x);
            int changeNum=m-res.z-res.z;
            ans[i]+=changeNum;
            res.y^=1;
            res.z+=changeNum;
            changeId(root[i],1,n,x,res);
        }
        else
        {
            scanf("%d",&x);
            root[i]=root[x];
            ans[i]=ans[x];
        }
        printf("%d\n",ans[i]);
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值