[航海协会]青蛙题

Posted StaroForgin

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[航海协会]青蛙题相关的知识,希望对你有一定的参考价值。

青蛙题

题解


题解

首先,看到 n ⩽ 2 × 1 0 6 n\\leqslant 2\\times 10^6 n2×106的数据范围,大概可以猜到,时间复杂度是 O ( n log ⁡ n ) O\\left(n\\log n\\right) O(nlogn)级别的,所以先刨除分块什么的算法。然而笔者场上打了个 O ( n n ) O\\left(n\\sqrtn\\right) O(nn )的回滚莫队

一种简单想法是求出所有的区间,把它们分别贡献到自己能够合法的所有区间上面。
但总区间是 O ( n 2 ) O\\left(n^2\\right) O(n2)级别的,好像不太可能可行。
我们不如扫描线去右端点,同时用线段树维护对于这个右端点合法,每个点作为选择子区间的左端点,至少要向右延伸多少。
因为是框定的左端点,所以我们也就只能使得左端点到右端点之间的所有数都出现,左端点左边的数就一辈子出现不了了。
在这个条件下,我们左端点所延伸出来的这个区间只会贡献到与它出现点集相同的,在左端点在它右边的询问。
我们考虑点集相同这个条件要怎么去维护,显然,从我们扫描线扫到的右端点开始往左走,我们算的是每个点到右端点之间这个区间的点集,显然它是递增的。
显然,如果两个点集大小相同的话,它们的点集也必然是相同的。

考虑如何维护点集的大小。
我们记 l a s t i last_i lasti表示 i i i上一个出现的位置,显然,区间 ( l a s t i , i ] (last_i,i] (lasti,i]区间的区间大小都会加一,因为它们都没有出现过这个数。
同样,这些左端点所选择的子区间的右端点都必须到达我们当前扫描到的 r r r
如果我们那线段树维护的话,我们线段树上要同时维护集合的大小与最大集合的大小对于的最短子区间的长度。
区间大小直接加都行了,但最长子区间的话,就必须还要记录下来最大集合的最右左端点,因为我们是区间赋的右端点,我们得用这个更新最短子区间。
之后就很简单了,排序后对于每个询问线段树上二分出它合法的子区间端点,区间询问得到答案。

时间复杂度 O ( n log ⁡ n ) O\\left(n\\log n\\right) O(nlogn)

源码

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int,int> pii;
typedef unsigned int uint;
#define MAXN 2000005
#define pb push_back
#define pf push_front
#define mkpr make_pair
#define fir first
#define sec second
#define lson (rt<<1)
#define rson (rt<<1|1)
const LL INF=0x3f3f3f3f3f3f3f3f;
const int mo=998244353;
template<typename _T>
void read(_T &x)
   _T f=1;x=0;char s=getchar();
   while(s<'0'||s>'9')if(s=='-')f=-1;s=getchar();
   while('0'<=s&&s<='9')x=(x<<3)+(x<<1)+(s^48);s=getchar();
   x*=f;

template<typename _T>
_T Fabs(_T x)return x<0?-x:x;
LL gcd(LL a,LL b)return !b?a:gcd(b,a%b);
int add(int x,int y,int p)return x+y<p?x+y:x+y-p;
void Add(int &x,int y,int p)x=add(x,y,p);
int qkpow(int a,int s,int p)int t=1;while(s)if(s&1)t=1ll*a*t%p;a=1ll*a*a%p;s>>=1;return t;
int n,q,a[MAXN],las[MAXN],ans[MAXN];
struct mingint l,r,id;s[MAXN];
bool cmp(ming x,ming y)return x.r<y.r;
class SegmentTree
    private:
        int maxx[MAXN<<2],minn[MAXN<<2],rig[MAXN<<2];
        int lzymx[MAXN<<2],lzyrg[MAXN<<2];
        void pushup(int rt)
            maxx[rt]=max(maxx[lson],maxx[rson]);minn[rt]=n;rig[rt]=0;
            if(maxx[rt]==maxx[lson])
                minn[rt]=min(minn[rt],minn[lson]),
                rig[rt]=max(rig[rt],rig[lson]);
            if(maxx[rt]==maxx[rson])
                minn[rt]=min(minn[rt],minn[rson]),
                rig[rt]=max(rig[rt],rig[rson]);
        
        void pushdown(int rt)
            if(lzymx[rt])
                maxx[lson]+=lzymx[rt];lzymx[lson]+=lzymx[rt];
                maxx[rson]+=lzymx[rt];lzymx[rson]+=lzymx[rt];
                lzymx[rt]=0;
            
            if(lzyrg[rt])
                minn[lson]=lzyrg[rt]-rig[lson]+1;lzyrg[lson]=lzyrg[rt];
                minn[rson]=lzyrg[rt]-rig[rson]+1;lzyrg[rson]=lzyrg[rt];
                lzyrg[rt]=0;
            
        
    public:
        void build(int rt,int l,int r)
            if(l==r)rig[rt]=l;return ;int mid=l+r>>1;
            build(lson,l,mid);build(rson,mid+1,r);pushup(rt);
        
        void modify(int rt,int l,int r,int al,int ar)
            if(l>r||l>ar||r<al||al>ar)return ;
            if(al<=l&&r<=ar)
                maxx[rt]++;minn[rt]=ar-rig[rt]+1;
                lzymx[rt]++;lzyrg[rt]=ar;return ;
            
            int mid=l+r>>1;pushdown(rt);
            if(al<=mid)modify(lson,l,mid,al,ar);
            if(ar>mid)modify(rson,mid+1,r,al,ar);
            pushup(rt);
        
        int query(int rt,int l,int r,int ai)
            if(l==r)return maxx[rt];
            int mid=l+r>>1;pushdown(rt);
            if(ai<=mid)return query(lson,l,mid,ai);
            return query(rson,mid+1,r,ai);
        
        int ask(int rt,int l,int r,int ak)
            if(l==r)return l;int mid=l+r>>1;pushdown(rt);
            if(maxx[rson]>=ak)return ask(rson,mid+1,r,ak);
            return ask(lson,l,mid,ak);
        
        int askMn(int rt,int l,int r,int al,int ar)
            if(l>r||l>ar||r<al||al>ar)return n;
            if(al<=l&&r<=ar)return minn[rt];
            int mid=l+r>>1,res=n;pushdown(rt);
            if(al<=mid)res=min(res,askMn(lson,l,mid,al,ar));
            if(ar>mid)res=min(res,askMn(rson,mid+1,r,al,ar));
            return res;
        
T;
int main()
    //freopen("frog.in","r",stdin);
    //freopen("frog.out","w",stdout);
    read(n);for(int i=1;i<=n;i++)read(a[i]);read(q);
    for(int i=1;i<=q;i++)read(s[i].l),read(s[i].r),s[i].id=i;
    sort(s+1,s+q+1,cmp);T.build(1,1,n);
    for(int i=1,j=1;i<=n;i++)
        T.modify(1,1,n,las[a[i]]+1,i);las[a[i]]=i;
        while(j<=q&&s[j].r==i)
            int l=s[j].l,tp=T.query(1,1,n,l),r=T.ask(1,1,n,tp);
            ans[s[j].id]=T.askMn(1,1,n,l,r);j++;
        
    
    for(int i=1;i<=q;i++)printf("%d\\n",ans[i]);
    return 0;

谢谢!!!

以上是关于[航海协会]青蛙题的主要内容,如果未能解决你的问题,请参考以下文章

[航海协会]逆天题

[航海协会]逆天题

[航海协会]逆天题

[航海协会]树

[航海协会]树

[航海协会]树