大概是一道带修莫队的裸题,然而还是WA了无数次,真是太弱了......
千万要记得带修的话前驱和后驱都要记录
都要记录!
要记录!
记录!
录!
!
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn=10005;
const int maxm=1000005;
struct query
{
int id,l,r,t,block;
}q[maxn];
struct change
{
int pre,sub,pos;//注意向前扫和向后扫变的值不一样,需要记录pre和sub
}c[maxn];
int n,m,cnt,tot;
int a[maxn];
int last[maxn];
int num[maxm];
int ans[maxn];
bool cmp(query x,query y)
{
return x.block<y.block||(x.block==y.block&&x.r<y.r)
||(x.block==y.block&&x.r==y.r&&x.t<y.t);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",a+i),last[i]=a[i];
int bl=(int)pow(n,2.0/3.0);
char in[5];
for(int i=1;i<=m;i++)
{
scanf("%s",in);
if(in[0]=='Q')
{
++cnt;
scanf("%d%d",&q[cnt].l,&q[cnt].r);
q[cnt].block=(q[cnt].l-1)/bl+1;
q[cnt].id=cnt;
q[cnt].t=tot;
}
else
{
++tot;
scanf("%d%d",&c[tot].pos,&c[tot].sub);
c[tot].pre=last[c[tot].pos];
last[c[tot].pos]=c[tot].sub;
}
}
sort(q+1,q+cnt+1,cmp);
int l=1,r=0,t=0,res=0;
for(int i=1;i<=cnt;i++)
{
while(q[i].t>t)
{
t++;
if(l<=c[t].pos&&c[t].pos<=r)
{
num[a[c[t].pos]]--;
if(!num[a[c[t].pos]])res--;
a[c[t].pos]=c[t].sub;
if(!num[a[c[t].pos]])res++;
num[a[c[t].pos]]++;
}
else
a[c[t].pos]=c[t].sub;
}
while(q[i].t<t)
{
if(l<=c[t].pos&&c[t].pos<=r)
{
num[a[c[t].pos]]--;
if(!num[a[c[t].pos]])res--;
a[c[t].pos]=c[t].pre;
if(!num[a[c[t].pos]])res++;
num[a[c[t].pos]]++;
}
else
a[c[t].pos]=c[t].pre;
t--;
}
while(l>q[i].l)
{
l--;
if(!num[a[l]])res++;
num[a[l]]++;
}
while(r<q[i].r)
{
r++;
if(!num[a[r]])res++;
num[a[r]]++;
}
while(l<q[i].l)
{
num[a[l]]--;
if(!num[a[l]])res--;
l++;
}
while(r>q[i].r)
{
num[a[r]]--;
if(!num[a[r]])res--;
r--;
}
ans[q[i].id]=res;
}
for(int i=1;i<=cnt;i++)
printf("%d\n",ans[i]);
return 0;
}
然后还有一个搜到的做法,学习了一个奇怪的树套树。
主席树中一个节点的修改会牵扯很多棵树,暴力修改的复杂度是O(nlogn),太过于庞大,于是采用一种机智的做法,利用lowbit的特性记录修改轨迹。
树状数组可以记录一个数向上更新的轨迹,本来数更新一个数组c[i],现在将这个更新改为更新一个主席树,值设为需要更新的数(注意修改节点的值)。查询时向下累加,计算牵扯的节点总共的修改总值,然后和原静态主席树的ans叠加即可得到修改后的正确值。
然后对于这道题,先搞个离散是毋庸置疑的。而查询[l,r]中的第一次出现的数的总个数,那么可以改为查询它的前驱在l之前的节点数有多少。在一棵以pos为下标的线段树上查询值小于l的个数有多少?很明显需要n查询。而思考一下对于一棵计数线段树,其下标为数值,查询[1,l]范围的数的个数只需要查询[1,l]范围内的sum,而对于一列数来说则需要一列线段树,用树状数组联系这一列线段树,支持其修改和查询操作。而这一列线段树的根是彩笔的位置,其上的叶节点表示当前彩笔颜色的前驱(即前一个颜色相同的彩笔的位置,建立的也是一个位置图),然后查询[1,r]范围内值小于l的,即每棵线段树求一下[1,l]的sum。要转化为题目要求的[l,r]区间,只需查分一下即可。
由于每个颜色会出现多次,每个颜色插入和查询都需要一棵平衡树,于是每个颜色建立set(STL真好233),注意当it为终结点的情况的判断。
N.B.注释内容为易错的小细节。
注意数据范围为10000+1000。。。
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<set>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=11005;
struct tree
{
int lson,rson,val;
}t[maxn*120];
int n,m,num,cnt;
int disc[maxn];
int a[maxn],last[maxn];
int root[maxn],c[maxn];
bool q[maxn];
pair<int,int>node[maxn];
set<int>col[maxn];
set<int>::iterator it,pre,sub;
void build_init(int &ro,int l,int r)//静态主席树
{
ro=++cnt;
if(l==r)return;
int mid=l+r>>1;
build_init(t[ro].lson,l,mid);
build_init(t[ro].rson,mid+1,r);
}
void build_seg(int pre,int &ro,int pos,int val,int l,int r)//静态主席树
{
ro=++cnt;
t[ro]=t[pre];//
t[ro].val+=val;
if(l==r)return;
int mid=l+r>>1;
if(pos<=mid)build_seg(t[pre].lson,t[ro].lson,pos,val,l,mid);
else build_seg(t[pre].rson,t[ro].rson,pos,val,mid+1,r);
}
void update_seg(int &ro,int pos,int val,int l,int r)//动态修改主席树
{
if(!ro)ro=++cnt;
t[ro].val+=val;
if(l==r)return;
int mid=l+r>>1;
if(pos<=mid)update_seg(t[ro].lson,pos,val,l,mid);
else update_seg(t[ro].rson,pos,val,mid+1,r);
}
int query_seg(int ro,int maxi,int l,int r)
{
if(r==maxi)return t[ro].val;
int mid=l+r>>1;
if(maxi<=mid)return query_seg(t[ro].lson,maxi,l,mid);
else return t[t[ro].lson].val+query_seg(t[ro].rson,maxi,mid+1,r);
}
void update_tree(int x,int pos,int val)
{
int tmp;
for(int i=x;i<=n;i+=i&-i)
update_seg(c[i],pos,val,0,n);
}
int query(int x,int maxi)
{
int res=query_seg(root[x],maxi,0,n);
for(int i=x;i;i-=i&-i)
res+=query_seg(c[i],maxi,0,n);
return res;
}
int main()
{
scanf("%d%d",&n,&m);
num=n;
for(int i=1;i<=n;i++)
scanf("%d",a+i),disc[i]=a[i];
char judge[2];
int x,y;
for(int i=1;i<=m;i++)
{
scanf("%s%d%d",judge,&node[i].first,&node[i].second);
if(judge[0]=='Q')
q[i]=true;
else
disc[++num]=node[i].second;
}
sort(disc+1,disc+num+1);
num=unique(disc+1,disc+num+1)-(disc+1);
for(int i=1;i<=n;i++)
a[i]=lower_bound(disc+1,disc+num+1,a[i])-disc;
build_init(root[0],0,n);
for(int i=1;i<=num;i++)col[i].insert(0);//
for(int i=1;i<=n;i++)
{
build_seg(root[i-1],root[i],last[a[i]],1,0,n);
last[a[i]]=i;
col[a[i]].insert(i);
}
for(int i=1;i<=m;i++)
{
if(q[i])
printf("%d\n",query(node[i].second,node[i].first-1)-query(node[i].first-1,node[i].first-1));
else
{
int pos=node[i].first;
int newcol=node[i].second;
newcol=lower_bound(disc+1,disc+num+1,newcol)-disc;
it=pre=sub=col[a[pos]].lower_bound(pos);
pre--;sub++;
update_tree(*it,*pre,-1);
if(sub!=col[a[pos]].end())
update_tree(*sub,*it,-1),
update_tree(*sub,*pre,1);
col[a[pos]].erase(it);
a[pos]=newcol;
col[a[pos]].insert(pos);
it=pre=sub=col[a[pos]].lower_bound(pos);
pre--;sub++;
update_tree(*it,*pre,1);
if(sub!=col[a[pos]].end())
update_tree(*sub,*it,1),
update_tree(*sub,*pre,-1);
}
}
return 0;
}