洛谷P5522 棠梨煎雪(线段树+状态压缩)

这里是点名被卡的$O(qnlogm)$做法

首先考虑对于两个串$s$和$t$,我们怎么合并得到$ans$(两天的总状态):

1. 若存在$s_i$,$t_i$分别为0,1,则返回无解;
2. 若$s_i=t_i$,则$ans_i=s_i$(这里涵盖了$s_i=t_i=?$的情况);
3. 否则,$s_i$,$t_i$,必定一个为0/1,一个为?,$ans_i$取0/1的那个。

不难发现合并可以用线段树维护,而$s$,$t$也不必存成字符串,直接当成3进制(为了卡常一般用4进制+位运算写)long long存,无解用一个特殊数(比如-1)存,最后答案就是$2^{\text{?的个数}}$。

时间复杂度$O(qnlogm)$,但是由于无解的情况存在所以跑不满。

n方过百万,暴力踩标算

#include<cstdio>
#include<cctype>
typedef long long ll;
const int N=100050;
char rB[1<<23],*rS,*rT;
inline char gc(){return rS==rT&&(rT=(rS=rB)+fread(rB,1,1<<23,stdin),rS==rT)?EOF:*rS++;}
inline char rdc(){
    char c=gc();
    while(isspace(c))c=gc();
    return c;
}
inline int rdi(){
    char c=gc();
    while(!isdigit(c))c=gc();
    int x=c&15;
    for(c=gc();isdigit(c);c=gc())x=(x<<3)+(x<<1)+(c&15);
    return x;
}
short n;
int x,y;
ll a[N],sum[N<<2],res;
ll merg(ll a,ll b){
    if(a==-1||b==-1)return -1;  //一个无解则必定无解
    ll s=0ll;
    for(short i=0;i<(n<<1);i+=2)if(((a>>i)&3ll)==2)s|=b&(3ll<<i);
    else if(((b>>i)&3ll)==2ll||((a>>i)&3ll)==((b>>i)&3ll))s|=a&(3ll<<i);
    else{s=-1;break;}
    return s;
}
void build(int o,int L,int R){
    if(L==R)sum[o]=a[L];
    else{
        int lc=o<<1,rc=lc|1,M=L+R>>1;
        build(lc,L,M);build(rc,M+1,R);
        sum[o]=merg(sum[lc],sum[rc]);
    }
}
void update(int o,int L,int R){
    if(L==R)sum[o]=res;
    else{
        int lc=o<<1,rc=lc|1,M=L+R>>1;
        if(x<=M)update(lc,L,M);
        else update(rc,M+1,R);
        sum[o]=merg(sum[lc],sum[rc]);
    }
}
ll ask(int o,int L,int R){
    if(x<=L&&y>=R)return sum[o];
    int lc=o<<1,rc=lc|1,M=L+R>>1;
    if(x<=M)if(y>M)return merg(ask(lc,L,M),ask(rc,M+1,R));
    else return ask(lc,L,M);
    else return ask(rc,M+1,R);
}
inline int query(ll x){  //求方案数
    if(x==-1)return 0;
    int s=1;
    for(short i=0;i<(n<<1);i+=2)if(((x>>i)&3ll)==2ll)s<<=1;
    return s;
}
int main(){
    int m,q,i,opt,ans=0;
    char c;
    short j;
    n=rdi();m=rdi();q=rdi();
    for(i=1;i<=m;++i){
        res=0ll;
        for(j=0;j<n;++j){
            c=rdc();
            res=(res<<2)|(c=='?'?2:(c&15));  //把30位压进long long里(正向反向无所谓)
        }
        a[i]=res;
    }
    build(1,1,m);
    while(q--){
        opt=rdi();x=rdi();
        if(!opt){
            y=rdi();
            ans^=query(ask(1,1,m));
        }else if(opt==1){
            res=0ll;
            for(j=0;j<n;++j){
                c=rdc();
                res=(res<<2)|(c=='?'?2:(c&15));
            }
            update(1,1,m);
        }
    }
    printf("%d",ans);
    return 0;
}
View Code

 

posted @ 2019-08-26 15:15  wangyuchen  阅读(140)  评论(0编辑  收藏  举报