luogu P1198 [JSOI2008]最大数
传送门:https://www.luogu.org/problemnew/show/P1198
由于每次只在最后加数我们就考虑一个神奇的动态反向ST表XD
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <ctime>
#define ll long long
#define maxn 200000+10
#define inf 0x7fffffff
using namespace std;
char opt;
int n,len,t;
ll mo,x,maxi[maxn][21];
void insert(int u,ll x)
{
maxi[u][0]=x;
for(int i=1;(1<<i)<=u;++i)
maxi[u][i]=max(maxi[u][i-1],maxi[u-(1<<(i-1))][i-1]);
}
ll query(int l,int r)
{
if(l>r) return maxi[r][0];
int k=log(r-l+1)/log(2);
return max(maxi[l+(1<<k)-1][k],maxi[r][k]);
}
int main()
{
scanf("%d%lld",&n,&mo);
for(int i=1;i<=n;++i)
{
cin>>opt;scanf("%lld",&x);
if(opt=='A') insert(++len,(x+t)%mo);
else printf("%lld\n",t=query(len-x+1,len));
}
return 0;
}
luogu P2161 [SHOI2009]会场预约
传送门:https://www.luogu.org/problemnew/show/P2161
线段树染色,用一个del数组记录该颜色有没有被删除,tag记录区间的颜色
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <ctime>
#define maxn 100010
using namespace std;
char opt[2];
bool del[maxn<<2],same[maxn<<2];
int ans1,ans2,n,cnt,st,ed;
int ll[maxn<<2],rr[maxn<<2],tag[maxn<<2];
void build(int l,int r,int num)
{
ll[num]=l,rr[num]=r;same[num]=true;
if(l==r) return;
int mid=(l+r)>>1;
build(l,mid,num<<1);
build(mid+1,r,num<<1|1);
}
void pushdown(int num)
{
same[num]=false;
if(!tag[num]) return;
tag[num<<1]=tag[num];
tag[num<<1|1]=tag[num];
tag[num]=0;
}
void modify(int l,int r,int num,int color)
{
if(ll[num]>r||rr[num]<l) return;
if(ll[num]>=l&&rr[num]<=r&&same[num])
{
if(tag[num]&&!del[tag[num]])
ans2--,ans1++,del[tag[num]]=true;
tag[num]=color;
return;
}
pushdown(num);
if(ll[num]>=l&&rr[num]<=r)
same[num]=true,tag[num]=color;
modify(l,r,num<<1,color);
modify(l,r,num<<1|1,color);
}
int main()
{
scanf("%d",&n);
build(1,maxn,1);
while(n--)
{
scanf("%s",opt);
switch(opt[0])
{
case 'A':
{
cnt++;ans2++;ans1=0;
scanf("%d %d",&st,&ed);
modify(st,ed,1,cnt);
printf("%d\n",ans1);
break;
}
case 'B':{printf("%d\n",ans2);break;}
}
}
return 0;
}
luogu P1972 [SDOI2009]HH的项链
传送门:https://www.luogu.org/problemnew/show/P1972
一开始是学莫队的时候来做这道题的,没想到丧心病狂的管理居然把数据加强了,暴力的莫队被卡成80啦!!
下面是莫队算法的代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <ctime>
#define maxn 1000010
#define maxm 1000010
using namespace std;
int n,m,blo,anss;
int pos[maxn],c[maxn],ans[maxn],cnt[maxn];
struct qwq
{
int ql,qr,id;
}q[maxm];
int read()
{
int xx=0,kk=1;char p=' ';
while(!isdigit(p)){p=getchar();if(p=='-')kk=-1;}
while(isdigit(p)){xx=xx*10+p-'0';p=getchar();}
return kk*xx;
}
bool cmp(qwq x,qwq y)
{
if(pos[x.ql]==pos[y.ql])
return x.qr<y.qr;
return x.ql<y.ql;
}
void add(int x)
{
if(++cnt[c[x]]==1) anss++;
}
void del(int x)
{
if(--cnt[c[x]]==0) anss--;
}
void solve()
{
int r=0,l=0;
for(int i=1;i<=m;++i)
{
while(l<q[i].ql)del(l++);
while(l>q[i].ql)add(--l);
while(r>q[i].qr)del(r--);
while(r<q[i].qr)add(++r);
ans[q[i].id]=anss;
}
}
int main()
{
n=read();
for(int i=1;i<=n;++i) c[i]=read();
blo=sqrt(n);
for(int i=1;i<=n;++i) pos[i]=(i-1)/blo+1;
m=read();
for(int i=1;i<=m;++i)
q[i].ql=read(),q[i].qr=read(),q[i].id=i;
sort(q+1,q+1+m,cmp);
solve();
for(int i=1;i<=m;++i) printf("%d\n",ans[i]);
return 0;
}
然后看题解发现正解居然是树状数组???可太巧妙了,果然莫队还是太暴力了
下面是大佬的题解(权侵删):
*对于若干个询问的区间[l,r],如果他们的r都相等的话,那么项链中出现的同一个数字,一定是只关心出现在最右边的那一个的,例如:
项链是:1 3 4 5 1
那么,对于r=5的所有的询问来说,第一个位置上的1完全没有意义,因为r已经在第五个1的右边,对于任何查询的[L,5]区间来说,如果第一个1被算了,那么他完全可以用第五个1来替代。
因此,我们可以对所有查询的区间按照r来排序,然后再来维护一个树状数组,这个树状数组是用来干什么的呢?看下面的例子:
1 2 1 3
对于第一个1,insert(1,1);表示第一个位置出现了一个不一样的数字,此时树状数组所表示的每个位置上的数字(不是它本身的值而是它对应的每个位置上的数字)是:1 0 0 0
对于第二个2,insert(2,1);此时树状数组表示的每个数字是1 1 0 0
对于第三个1,因为之前出现过1了,因此首先把那个1所在的位置删掉insert(1,-1),然后在把它加进来insert(3,1)。此时每个数字是0 1 1 0
如果此时有一个询问[2,3],那么直接求sum(3)-sum(2-1)=2就是答案。*
然后是南瓜的辣鸡代码啦qwq
#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <ctime>
#define maxn 1000010
using namespace std;
int n,m,now;
int a[maxn],c[maxn],ans[maxn],pos[maxn];
struct qwq
{
int l,r,id;
}q[maxn];
int read()
{
int xx=0,kk=1;char ch=' ';
while(!isdigit(ch)){ch=getchar();if(ch=='-')kk=-1;}
while(isdigit(ch)){xx=xx*10+ch-'0';ch=getchar();}
return kk*xx;
}
bool cmp(qwq x,qwq y)
{
return x.r==y.r?(x.l<y.l):(x.r<y.r);
}
int lowbit(int x)
{
return x&-x;
}
void add(int x,int k)
{
for(x;x<=n;x+=lowbit(x))
c[x]+=k;
}
int query(int x)
{
int ans=0;
for(x;x;x-=lowbit(x))
ans+=c[x];
return ans;
}
int main()
{
n=read();
for(int i=1;i<=n;++i)
a[i]=read();
m=read();
for(int i=1;i<=m;++i)
q[i].l=read(),q[i].r=read(),q[i].id=i;
sort(q+1,q+1+m,cmp);
for(int i=1;i<=m;++i)
{
while(now<q[i].r)
{
now++;
if(pos[a[now]]) add(pos[a[now]],-1);
pos[a[now]]=now;add(now,1);
}
ans[q[i].id]=query(q[i].r)-query(q[i].l-1);
}
for(int i=1;i<=m;++i)
printf("%d\n",ans[i]);
return 0;
}