[题目链接] (https://acm.xylab.fun/judge/problem/view/1028)
题意:对于一个1-n的序列,有两种操作,第一种更改一个位置的值第二种就是查询[l,r]里x出现奇数次的子区间的个数。
题解:我们对每个位置的数建一颗区间01线段树,那么我们动态开点,注意这里有多个根,也就是说有很多颗线段树。那么我们可以维护五个值:val,len,sumL,sumR,sum,分别是线段树节点合法区间个数,区间长度,包含左端点的合法区间个数,包含右端点的合法区间个数,区间和。最关键应该考虑的是区间的合并,val = ls[o].val + rs[o].val , val += sumR*(len-sumL),val += (len-sumR) * sumL ;那么sumL,sumR怎么合并,请读者思考。
#include
#define ll long long
using namespace std;
const int maxn = 4e5+10;
int a[maxn],cnz=0,cnt=0;
mapmp;
struct node {
ll val;
int len, sumL, sumR, sum;
node operator+(const node &t) const {
node tmp;
tmp.val = val + t.val;
tmp.val += 1ll * sumR * (t.len - t.sumL);
tmp.val += 1ll * (len - sumR) * t.sumL;
tmp.len = len + t.len;
tmp.sum = sum + t.sum;
if (sum % 2)
tmp.sumL = sumL + t.len - t.sumL;
else
tmp.sumL = sumL + t.sumL;
if (t.sum % 2)
tmp.sumR = t.sumR + len - sumR;
else
tmp.sumR = t.sumR + sumR;
return tmp;
}
} tr[maxn * 24];
int ls[maxn*24],rs[maxn*24],rt[maxn*2];
void init(int& o,int len){
o = ++cnt;
tr[o].len = len;
}
void up(int& o,int l,int r,int k,int p)
{
if(!o) o = ++cnt;
if(l==r)
{
tr[o].val = tr[o].sumL = tr[o].sumR = tr[o].sum = p;
tr[o].len = 1;
return ;
}
int m = (l+r)/2;
if(k<=m) up(ls[o],l,m,k,p);
else up(rs[o],m+1,r,k,p);
if(!ls[o])init(ls[o],m-l+1);
if(!rs[o])init(rs[o],r-m);
tr[o] = tr[ls[o]] + tr[rs[o]];
}
node qu(int& o,int l,int r,int ql,int qr)
{
if(!o) init(o,r-l+1);
if(ql<=l&&qr>=r)return tr[o];
int m=(l+r)/2;
if(ql>m) return qu(rs[o],m+1,r,ql,qr);
else if(qr<=m) return qu(ls[o],l,m,ql,qr);
else return qu(ls[o],l,m,ql,qr) + qu(rs[o],m+1,r,ql,qr);
}
int main()
{
int n;scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
if(!mp[a[i]])
mp[a[i]] = ++cnz;
up(rt[mp[a[i]]],1,n,i,1);
}
int q;scanf("%d",&q);
while(q--)
{
int op;scanf("%d",&op);
if(op==1)
{
int p,v;
scanf("%d%d",&p,&v);
up(rt[mp[a[p]]],1,n,p,0);
a[p] = v;
if(!mp[a[p]])
mp[a[p]] = ++cnz;
up(rt[mp[a[p]]],1,n,p,1);
}
else {
int l,r,x;
scanf("%d%d%d",&l,&r,&x);
printf("%lld\n",qu(rt[mp[x]],1,n,l,r).val);
}
}
}
如果理解动态开点 线段树的,应该很容易看懂,提醒一下,init()函数就是因为你合并区间长度的时候,有些点没开,所以要给一些点区间长度。