test3 Problem C: [noip2016十连测第三场]序列 (主席树)

Problem C: [noip2016十连测第三场]序列

Time Limit: 10 Sec   Memory Limit: 256 MB
Submit: 61   Solved: 25
[ Submit][ Status][ Web Board]

Description

小A把自己之前得到的序列展示给了小B,不过这一次,他并不要求小B模仿他之前的行为。他给了小B一些询问,每
个询问都是lrx的形式,要求小B数出在序列的第l个到第r个元素中有多少是不小于x的。小B很快就算出来了。小A
很不甘心,于是要求动态修改这个序列……这样,他只要求每次修改后求出所有询问答案的和即可。然而小B还是
很快就算出来了,小A很生气,于是把问题抛给了你。

Input

由于一些原因,本题采取一定的方式加密了修改操作。第一行三个整数nmq,分别表示序列长度、询问个数和修改
次数。第二行n个正整数描述序列。接下来m行每行三个数lrx,表示一次询问。最后q行每行两个数pv,表示把p^la
stans这个位置上的数修改成v^lastans(其中lastans指上次修改之后的答案,初始即为没有修改过的原序列的询
问答案,^为异或符号,C/C++中为^,pascal中为xor)。
【 数据范围与约定】
对于 20%的数据, n,m,q≤100
对于 40%的数据, n,m,q≤1000
对于 100%的数据, n,m,q≤100000, 序列中的数( 包括修改后的)
均为正数且不超过 n, 保证数据合法。

Output

q+1行每行一个整数,第一行表示原序列的所有询问答案之和,后面q行表示每次修改之后的序列的所有询问答案之
和。

Sample Input

4 2 2
1 4 2 3
2 4 3
1 3 2
6 6
2 7

Sample Output

4 
3 
4

HINT

[ Submit][ Status]

题解:主席树

我们先考虑如何预处理出最初的答案,我们要查询区间中大于等于x的数的个数,那么应该很容易想到这个可以用静态主席树来求解。将每个位置的数按顺序加入主席树(权值套区间)中,每次都是在前一颗树的基础上更新一条树链,这样的其实每个位置都是维护的前缀和,树的形态相同自然可以用作差的方式求区间和。

对于每一个更改我们考虑对答案的影响。设a>b,如果是把a改成b那么对答案产生的影响就是应该减去x的范围在(b,a]的包含当前位置的询问数,如果是从b改成a,那么对答案的影响就是应该加上x范围在(b,a]的包含当前位置的询问数。这个有关x的区间询问数我们还是可以用静态主席树来维护,我们把x从小到大排序,依次加入x=i(i=1...n)的询问,与上一颗主席树记录的信息不同的一点时,有且只有在当前区间完全包含在[l,r]范围内的时候区间答案才+1,这样在查找一个位置的时候需要将路径上经过的区间的答案累加就能得到答案。在继承上一个节点的信息的时候,因为一个i可能会对应多个询问,所以如果你在加入的时候当前节点不是上一颗树的节点,就不要继承了,直接在这上面累加就好。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define N 100003
#define LL long long 
using namespace std;
int n,m,q,sz;
int tr[N*50],ls[N*50],rs[N*50];
LL ans,sum[N*50];
int val[N],root[N],x[N],y[N],z[N];
struct data
{
	int x,y,z;
}a[N];
int cmp(data a,data b)
{
	return a.z<b.z;
}
void cover(int x,int y)
{
	tr[x]=tr[y]; ls[x]=ls[y]; rs[x]=rs[y]; sum[x]=sum[y]; 
}
void change(int &i,int l,int r,int x)
{
	cover(++sz,i);  i=sz;
	sum[i]++;
	if (l==r) return;
	int mid=(l+r)/2;
	if (x<=mid) change(ls[i],l,mid,x);
	else change(rs[i],mid+1,r,x);
}
LL qjsum(int i,int j,int l,int r,int ll,int rr)
{
	if (ll<=l&&r<=rr) return sum[i]-sum[j];
	int mid=(l+r)/2;
	LL ans=0;
	if (ll<=mid) ans+=qjsum(ls[i],ls[j],l,mid,ll,rr);
	if (rr>mid) ans+=qjsum(rs[i],rs[j],mid+1,r,ll,rr);
	return ans;
}
void qjchange(int &i,int l,int r,int x,int ll,int rr,int mark)
{
	if (!mark||i<x){
	  cover(++sz,i);  i=sz;
    }
	if (ll<=l&&r<=rr)  {
	   sum[i]++; 
	   return ;
    }
	int mid=(l+r)/2;
	if (ll<=mid) qjchange(ls[i],l,mid,x,ll,rr,mark);
	if (rr>mid) qjchange(rs[i],mid+1,r,x,ll,rr,mark);
}
LL qjask(int i,int j,int l,int r,int x)
{
	if (l==r) return sum[i]-sum[j];
	int mid=(l+r)/2;
	LL ans=sum[i]-sum[j];
	if (x<=mid) ans+=qjask(ls[i],ls[j],l,mid,x);
	else  ans+=qjask(rs[i],rs[j],mid+1,r,x);
	return ans;
}
int main()
{
	freopen("a.in","r",stdin);
	freopen("my.out","w",stdout);
	scanf("%d%d%d",&n,&m,&q);
	root[0]=0;
	for (int i=1;i<=n;i++)
	{
		scanf("%d",&val[i]);
		root[i]=root[i-1]; change(root[i],1,n,val[i]);
	}
	for (int i=1;i<=m;i++)
	{
		scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z);
		ans+=qjsum(root[a[i].y],root[a[i].x-1],1,n,a[i].z,n);
	}
	printf("%lld\n",ans);
	memset(sum,0,sizeof(sum)); memset(tr,0,sizeof(tr));
	memset(ls,0,sizeof(ls)); memset(rs,0,sizeof(rs));
	root[0]=0; sz=0;
	sort(a+1,a+m+1,cmp);
	int now=1;
	for (int i=1;i<=n;i++)
	{
		root[i]=root[i-1];
		int j=now;
		if (now>m) break;
		if (a[now].z!=i) continue; 
		while (a[j].z==a[now].z&&j<=m)  j++;
		qjchange(root[i],1,n,0,a[now].x,a[now].y,0);
		for (int k=now+1;k<j;k++)
		 qjchange(root[i],1,n,root[i],a[k].x,a[k].y,1); 
		now=j;
	}
	for (int i=a[m].z+1;i<=n;i++) root[i]=root[i-1];
	for (int i=1;i<=q;i++)
	{
		LL pos,k; scanf("%lld%lld",&pos,&k);
		pos^=ans; k^=ans;
		if (val[pos]<k)  ans+=qjask(root[k],root[val[pos]],1,n,(int)pos);
		if (val[pos]>k)  ans-=qjask(root[val[pos]],root[k],1,n,(int)pos);
		val[pos]=k;
		printf("%lld\n",ans);
	}
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值