P5105 不强制在线的动态快速排序
题目背景
曦月最近学会了快速排序,但是她很快地想到了,如果要动态地排序,那要怎么办呢?
题目描述
为了研究这个问题,曦月提出了一个十分简单的问题
曦月希望维护一个允许重复的集合\(S\),支持:
- 插入\([L, R]\),也就是插入\(L, L + 1 ... , R\),这\(R - L + 1\)个数
- 询问\(Sort(S)\)
\(Sort(S)\)的定义为:
我们将集合\(S\)中的元素从小到大按照快速排序排好序,记为\(a_1, a_2 ... a_n\)
那么,\(Sort(S) = \bigoplus \limits_{i = 2}^n (a_i^2 - a_{i - 1}^2)\),其中\(\bigoplus\)表示异或和
关于异或的定义,请咨询度娘
输入输出格式
输入格式:
第一行,一个数\(q\)
后\(q\)行,或者是\(1\;l\;r\),表示插入\([l, r]\),或者是\(2\),表示一次询问
输出格式:
对于每个询问,一行一个答案
说明
对于\(30\)分的数据,\(q \leqslant 100\)
对于\(50\)分的数据,\(q \leqslant 5 * 10^4\)
对于另外的\(20\)分的数据,满足\(L=R\)
对于\(100\)分的数据,\(q \leqslant 3 * 10^5\),\(1 \leqslant L \leqslant R \leqslant 10^9\)
先考虑单独插入一个区间\([l,r]\)
然后发现贡献是\(\bigoplus_{i=l}^r 2i+1\)
把每个贡献右移一位,可以用发现是求\(\bigoplus_{i=l}^r i\),然后特判一下最后一位的奇偶。
异或前缀和有个结论
int cal(int n)
{
if(n%4==0) return n;
if(n%4==1) return 1;
if(n%4==2) return n+1;
if(n%4==3) return 0;
}
多组显然可以平衡树,但是写起来很麻烦
于是可以用线段树直接维护区间插入,对每个区间维护最左和最右位置,然后直接合并左右儿子时计算一下贡献就可以了。
Code:
#include <cstdio>
#define int long long
const int N=3e5+10;
int cal(int n)
{
if(n%4==0) return n;
if(n%4==1) return 1;
if(n%4==2) return n+1;
if(n%4==3) return 0;
}
#define ls ch[now][0]
#define rs ch[now][1]
int sum[N*20],ch[N*20][2],lp[N*20],rp[N*20],is[N*20],tot,root;
void change(int &now,int L,int R,int l,int r)
{
if(is[now]) return;
if(!now) now=++tot;
if(L==l&&R==r)
{
is[now]=1;
if(l!=r) sum[now]=((cal(r-1)^cal(l-1))<<1)|(r-l&1);
lp[now]=l;
rp[now]=r;
return;
}
int Mid=L+R>>1;
if(r<=Mid) change(ls,L,Mid,l,r);
else if(l>Mid) change(rs,Mid+1,R,l,r);
else change(ls,L,Mid,l,Mid),change(rs,Mid+1,R,Mid+1,r);
lp[now]=lp[ls];
if(!lp[now]) lp[now]=lp[rs];
rp[now]=rp[rs];
if(!rp[now]) rp[now]=rp[ls];
sum[now]=sum[ls]^sum[rs];
if(rp[ls]&&lp[rs])
sum[now]^=(lp[rs]+rp[ls])*(lp[rs]-rp[ls]);
}
const int M=1e9;
signed main()
{
int n;scanf("%lld",&n);
for(int op,l,r,i=1;i<=n;i++)
{
scanf("%lld",&op);
if(op==2)
printf("%lld\n",sum[root]);
else
{
scanf("%lld%lld",&l,&r);
change(root,1,M,l,r);
}
}
return 0;
}
2018.12.16