[DS记录]P5312 [Ynoi2011] 竞赛实验班

command_block

2021-06-29 09:55:32

Personal

**题意** : 给出长为 $n$ 的数组 $A$ ,支持下列操作 : 1. 在数组 $A$ 的末尾添加一个数 $x$。 2. 区间求和。 3. 将数组 $A$ 中的每个数 $A_i$ 都改为 $A_i\oplus x$。($\oplus$ 表示异或操作)。 4. 将数组 $A$ 从小到大排序。 $n,m\leq 10^5$ ,时限$\texttt{1s}$,空限$\texttt{500M}$。 ------------ 若不存在操作 3 ,序列肯定形如一段排好序的以及一段未排序的,而且排好序的段只会延长。 对于排好序的数,用权值线段树维护,区间求和可以转化为前 $k$ 小求和。 对于未排序的部分,直接用树状数组维护。 接下来考虑操作 3。 记下两类标记,一类是全局异或标记 $t$,一类是排序段排序时的异或标记 $t'$。 当 `push_bacl` 一个数 $x$ 时,要将其异或 $t$。 用 01Trie 维护排好序的部分,查询前 $k$ 小时根据 $t'$ 将行走的方向取反。 我们要支持查询子树在异或 $t$ 后的和,于是要维护每一位的 $1$ 的个数。为了节省空间,只有一个儿子的点的信息可以引用儿子的信息。即压缩 Trie。 对于未排序的部分,用前缀和维护即可。 在全局排序时,只需将未排序的部分插入 Trie 中。 时间复杂度 $O(nw^2)$ ,空间复杂度 $O(nw)$。 ```cpp #include<algorithm> #include<cstdio> #define ll long long #define MaxN 100500 using namespace std; struct Buf{ int c[30],tot; void clr() {tot=0;for (int i=0;i<30;i++)c[i]=0;} ll calc(int x){ ll ret=0; for (int i=0;i<30;i++) ret+=((ll)(x>>i&1 ? tot-c[i] : c[i]))<<i; return ret; } }t[MaxN<<2];int tot; Buf get(int x){ Buf C;C.tot=1; for (int k=0;k<30;k++) C.c[k]=x>>k&1; return C; } Buf operator + (const Buf &A,const Buf &B){ Buf C;C.tot=A.tot+B.tot; for (int i=0;i<30;i++)C.c[i]=A.c[i]+B.c[i]; return C; } struct Node{int l,r,p;}a[MaxN<<6]; int tn; inline void up(int u){ if (a[u].l&&a[u].r){ if (a[u].p==a[a[u].l].p||a[u].p==a[a[u].r].p||!a[u].p)a[u].p=++tot; t[a[u].p]=t[a[a[u].l].p]+t[a[a[u].r].p]; }else a[u].p=a[a[u].l|a[u].r].p; } void add(int &u,int k,int x) { if (!u)u=++tn; if (k==-1){ if (!a[u].p)a[u].p=++tot; t[a[u].p].tot++; for (int i=0;i<30;i++) t[a[u].p].c[i]+=x>>i&1; return ; } if (x>>k&1)add(a[u].r,k-1,x); else add(a[u].l,k-1,x); up(u); } int wfk,tag,tag2;ll ret; void qry(int u,int k,int x) { if (k==-1){ret+=1ll*(tag^x)*wfk;return ;} if (tag2>>k&1){ int rc=t[a[a[u].r].p].tot; if (wfk>rc){ wfk-=rc; ret+=t[a[a[u].r].p].calc(tag); qry(a[u].l,k-1,x); }else qry(a[u].r,k-1,x|(1<<k)); }else { int lc=t[a[a[u].l].p].tot; if (wfk>lc){ wfk-=lc; ret+=t[a[a[u].l].p].calc(tag); qry(a[u].r,k-1,x|(1<<k)); }else qry(a[u].l,k-1,x); } } Buf o[MaxN<<1]; int n,m,p1,p2,rt,x[MaxN<<1]; int main() { scanf("%d",&n);p2=n; for (int i=1;i<=n;i++){ scanf("%d",&x[i]); o[i]=o[i-1]+get(x[i]); } scanf("%d",&m); for (int qt=1,op,l,r,val;qt<=m;qt++){ scanf("%d",&op); if (op==1){ scanf("%d",&x[++p2]); o[p2]=o[p2-1]+get(x[p2]^=tag); } if (op==2){ scanf("%d%d",&l,&r); int tl=l,tr=min(r,p1); ll ans=0; if (tl<=tr){ wfk=tl-1;ret=0;qry(rt,29,0);ans-=ret; wfk=tr;ret=0;qry(rt,29,0);ans+=ret; } tl=max(p1+1,l);tr=r; if (tl<=tr){ ans-=o[tl-1].calc(tag); ans+=o[tr].calc(tag); }printf("%lld\n",ans); } if (op==3){scanf("%d",&val);tag^=val;} if (op==4){ for (int i=p1+1;i<=p2;i++)add(rt,29,x[i]); p1=p2;tag2=tag; } }return 0; } ```