数据结构专题qvq

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;
}

猜你喜欢

转载自blog.csdn.net/qq_40942982/article/details/82347304
今日推荐