讲序列分成三部分,大根堆,缓冲区s,小根堆。
任意时刻保证mid在缓冲区中,并且尽量保证大根堆和小根堆的大小尽量相等。
均摊时间复杂度为 O(nlogn/s+n)
#include
#include
#include
#include
#include
#define LL long long
#define mod 1000000007
using namespace std;
int n,a,c[203];
priority_queue<int> q;
priority_queue<int,vector<int>,greater<int> > p;
int get(LL a,LL ans)
{
LL t=(1714636915*a+1681692777)%mod;
LL t1=(846930886*ans+1804289383)%mod;
return (int) (t*t1%mod);
}
int main()
{
freopen("mid.in","r",stdin);
freopen("mid.out","w",stdout);
scanf("%d%d",&n,&a);
int s=200; int cnt=0; int lastans=a; int ans=a;
c[++cnt]=a;
for (int i=2;i<=min(n,s);i++){
a=get(a,lastans);
int pos=cnt+1;
for (int j=1;j<=cnt;j++)
if (c[j]>=a) {
pos=j;
break;
}
for (int j=cnt+1;j>=pos+1;j--) c[j]=c[j-1];
c[pos]=a; cnt++;
lastans=c[(i+1)/2];
ans^=lastans;
}
int mid=(s+1)/2;
for (int i=s+1;i<=n;i++) {
a=get(a,lastans);
if (a<=c[1]) {
q.push(a);
if (!(i&1)) mid--;
if (mid==0) {
int t=q.top(); q.pop();
p.push(c[s]);
for (int j=s;j>=2;j--) c[j]=c[j-1];
c[1]=t;
mid++;
}
}
else if (a>=c[s]) {
p.push(a);
if (i&1) mid++;
if (mid==s+1) {
int t=p.top(); p.pop();
q.push(c[1]);
for (int j=1;j<s;j++) c[j]=c[j+1];
c[s]=t;
mid--;
}
}
else {
int pos;
for (int j=1;j<=s;j++)
if (c[j]>a) {
pos=j;
break;
}
if (p.size()>q.size()){
q.push(c[1]);
for (int j=1;j<pos-1;j++) c[j]=c[j+1];
c[pos-1]=a;
if (!(i&1)) mid--;
if (mid==0) {
int t=q.top(); q.pop();
p.push(c[s]);
for (int j=s;j>=2;j--) c[j]=c[j-1];
c[1]=t;
mid++;
}
}
else {
p.push(c[s]);
for (int j=s;j>=pos+1;j--) c[j]=c[j-1];
c[pos]=a;
if (i&1) mid++;
if (mid==s+1) {
int t=p.top(); p.pop();
q.push(c[1]);
for (int j=1;j<s;j++) c[j]=c[j+1];
c[s]=t;
mid--;
}
}
}
lastans=c[mid];
ans^=lastans;
}
printf("%d\n",ans);
}
首先打表发现sg函数的规律。
(1) p为奇数,从0开始sg值01循环
要统计区间的答案其实就是统计区间中sg值为1的数的个数。
用线段树维护区间中偶数/奇数的个数其实就是维护01的个数。
如果加入的数为偶数,区间中数的奇偶性不变,否则奇偶性翻转,维护翻转标记即可。
(2) p为偶数,从0开始循环节为(p+1).若i%(p+1)==p,则sg[i]=2.
若i%(p+1)为奇数,则sg[i]为1,否则为0.
偶数的话需要分块维护。
对于每个块,维护%(p+1)为奇数的数和偶数的数。
并保证偶数与奇数的两个余数数组有序。
对于修改,如果是整块就直接修改块的标记,散块暴力修改。
对于查询,散块直接暴力记录答案。整块的话,因为我们要找i%(p+1)==p的个数,即sg为2的个数。根据p-delta的奇偶性,在两个数组中查找。
因为模数为奇数,所以加入一个数取模后奇偶性可能会发生改变,发生改变的一定是两个数组的一个后缀,所以我们可以二分找到改变的位置。然后对delta的奇偶性分开讨论即可。具体过程见代码。
#include
#include
#include
#include
#define N 100003
using namespace std;
int a[N],n,m,p;
int cnt0[N*4],cnt1[N*4],delta[N*4],l[N],r[N],belong[N];
int calc(int x)
{
if (!(p&1)) {
if (x%(p+1)==p) return 2;
if (x%(p+1)%2==1) return 1;
return 0;
}
return x%2;
}
void update(int now)
{
cnt0[now]=cnt0[now<<1]+cnt0[now<<1|1];
cnt1[now]=cnt1[now<<1]+cnt1[now<<1|1];
}
void build(int now,int l,int r)
{
if (l==r) {
if (a[l]&1) cnt1[now]++;
else cnt0[now]++;
return;
}
int mid=(l+r)/2;
build(now<<1,l,mid);
build(now<<1|1,mid+1,r);
update(now);
}
void change(int now)
{
swap(cnt1[now],cnt0[now]);
delta[now]^=1;
}
void pushdown(int now)
{
if (delta[now]) {
change(now<<1);
change(now<<1|1);
delta[now]=0;
}
}
void qjchange(int now,int l,int r,int ll,int rr)
{
if (ll<=l&&r<=rr) {
change(now);
return;
}
int mid=(l+r)/2;
pushdown(now);
if (ll<=mid) qjchange(now<<1,l,mid,ll,rr);
if (rr>mid) qjchange(now<<1|1,mid+1,r,ll,rr);
update(now);
}
int query(int now,int l,int r,int ll,int rr)
{
if (ll<=l&&r<=rr) return cnt1[now];
int mid=(l+r)/2; int ans=0;
pushdown(now);
if (ll<=mid) ans+=query(now<<1,l,mid,ll,rr);
if (rr>mid) ans+=query(now<<1|1,mid+1,r,ll,rr);
return ans;
}
struct data{
int cnt0,cnt1,delta;
int odd[400],even[400];
}b[400];
void solve(int L,int R,int t,int x)
{
for (int i=l[t];i<=r[t];i++) a[i]+=b[t].delta,a[i]%=(p+1);
b[t].delta=0;
for (int i=L;i<=R;i++) a[i]+=x,a[i]%=(p+1);
b[t].cnt0=0; b[t].cnt1=0;
for (int i=l[t];i<=r[t];i++) {
if (a[i]&1) b[t].even[++b[t].cnt1]=a[i];
else b[t].odd[++b[t].cnt0]=a[i];
}
sort(b[t].even+1,b[t].even+b[t].cnt1+1);
sort(b[t].odd+1,b[t].odd+b[t].cnt0+1);
}
int get(int L,int R,int t)
{
int ans=0;
for (int i=L;i<=R;i++) {
ans^=calc(a[i]+b[t].delta);
}
return ans;
}
int findl(int *a,int x,int val)
{
int l=1; int r=x; int ans=x+1;
while (l<=r) {
int mid=(l+r)/2;
if (a[mid]>=val) ans=min(ans,mid),r=mid-1;
else l=mid+1;
}
return ans;
}
int findr(int *a,int x,int val)
{
int l=1; int r=x;int ans=0;
while (l<=r) {
int mid=(l+r)/2;
if (a[mid]<=val) ans=max(ans,mid),l=mid+1;
else r=mid-1;
}
return ans;
}
int main()
{
freopen("right.in","r",stdin);
freopen("right.out","w",stdout);
scanf("%d%d%d",&n,&m,&p);
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
if (p&1) {
build(1,1,n);
for (int i=1;i<=m;i++) {
int opt,l,r,x;
scanf("%d%d%d",&opt,&l,&r);
if (opt==0) {
scanf("%d",&x);
if (x%2) qjchange(1,1,n,l,r);
}
else {
int t=query(1,1,n,l,r);
if (t&1) printf("1\n");
else printf("0\n");
}
}
return 0;
}
int blocksize=sqrt(n); int mx=0;
for (int i=1;i<=n;i++) {
belong[i]=(i-1)/blocksize+1;
if (l[belong[i]]==0) l[belong[i]]=i;
r[belong[i]]=i;
mx=belong[i];
}
for (int i=1;i<=mx;i++) {
for (int j=l[i];j<=r[i];j++) {
a[j]%=(p+1);
if (a[j]&1) b[i].even[++b[i].cnt1]=a[j];
else b[i].odd[++b[i].cnt0]=a[j];
}
sort(b[i].even+1,b[i].even+b[i].cnt1+1);
sort(b[i].odd+1,b[i].odd+b[i].cnt0+1);
}
for (int i=1;i<=m;i++) {
int opt,l1,r1,x;
scanf("%d%d%d",&opt,&l1,&r1);
if (opt==0) {
scanf("%d",&x);
int L=belong[l1]; int R=belong[r1];
if (L==R) {
solve(l1,r1,L,x);
continue;
}
for (int j=L+1;j<=R-1;j++) b[j].delta+=x,b[j].delta%=(p+1);
solve(l1,r[L],L,x);
solve(l[R],r1,R,x);
}
else {
int ans=0;
int L=belong[l1]; int R=belong[r1];
if (L==R) {
ans=get(l1,r1,L);
printf("%d\n",ans?1:0);
continue;
}
for (int j=L+1;j<=R-1;j++) {
int s1=(p-b[j].delta+p+1)%(p+1);
int t=0;
if (s1&1) t=findr(b[j].even,b[j].cnt1,s1)-findl(b[j].even,b[j].cnt1,s1)+1;
else t=findr(b[j].odd,b[j].cnt0,s1)-findl(b[j].odd,b[j].cnt0,s1)+1;
if (t&1) ans^=2;
int t1=0; int s=0;
if (b[j].delta&1) {
s=p+2-b[j].delta;
t1+=b[j].cnt1-findl(b[j].even,b[j].cnt1,s)+1;
if ((s1&1)&&(s1>=s)) t1-=t;
s=p-b[j].delta;
t1+=findr(b[j].odd,b[j].cnt0,s);
if (!(s1&1)&&(s1<=s)) t1-=t;
}
else {
s=p-b[j].delta;
t1+=findr(b[j].even,b[j].cnt1,s);
if ((s1&1)&&(s1<=s)) t1-=t;
s=p+2-b[j].delta;
t1+=b[j].cnt0-findl(b[j].odd,b[j].cnt0,s)+1;
if (!(s1&1)&&(s1>=s)) t1-=t;
}
if (t1&1) ans^=1;
}
ans^=get(l1,r[L],L);
ans^=get(l[R],r1,R);
printf("%d\n",ans?1:0);
}
}
}