• [Contest on 2021.8.30] 人已经麻了


    ( ext{Mex})

    题目描述

    给定长度为 (n) 的序列,有 (q) 个询问 ((l,r)),对于每个询问,你需要回答 ( ext{mex}{a_l,a_{l+1},...,a_r})

    (1le n,qle 2cdot 10^5)(0le a_ile 2cdot 10^5)

    解法

    这题 (sf ET) 说至少做过两次,可是我真的毫无印象… 那么只有 —— 馍馍馍 (sf ET)

    离线

    很妙的一点是用到了 ( m mex) 的删除非常简单!如果在集合中添加数则很难维护答案的变化;而删除数则只用维护删除的最小值,更大的都不可能是答案。

    把所有询问塞到以左端点为下标的 vector 里。考虑先计算出左端点为 (1) 的所有答案,可以用 set 从右到左删除来维护,除此之外,再预处理出 (nxt_i) 表示在 (i) 之后下一个出现 (a_i) 的位置。

    这样从 (1) 开始从左到右删除点,将 (a_i) 与右端点在 ((i,nxt_i)) 的答案取 (min) 即可。

    在线

    以区间为根的下标,搞 (n) 棵可持久化权值线段树。从左到右枚举根,在权值线段树内部维护某个权值 (val) 当前最大的下标。对于查询 ((l,r)),在 (r) 号根中进行二分,如果左儿子所有权值最大下标的 (min<l),说明左儿子中存在答案,往左儿子递归;反之往右儿子递归,因为值域大小 (2cdot 10^5+1) 大于序列长度 (2cdot 10^5),所以不用考虑超出的问题。

    ( ext{CF1422F Boring Queries})

    解法

    首先比较容易想到的是,将小于 (sqrt {a_i}) 的质数暴力用线段树维护指数 (max),大概是 (mathcal O(86cdot nlog n))

    关键是处理大于 (sqrt {a_i}) 的质数,也就是询问区间不同数字的积。对于此类询问我们一般会搞一个 (pre_i/nxt_i) 来表示在 (i) 之前或之后第一个权值为 (a_i) 的下标,从而去重。下面讲的三种方案也是基于此来解决的。

    我会主席树!

    对于询问 ((l,r)),如果 (iin[l,r])(pre_i<l),就将 (a_i) 加入贡献,否则不予处理。一个惊喜的发现是,对于 (jin [0,n]),只会有一个 (i) 满足 (pre_i=j),也即如果以 (j) 为下标可以建立主席树。

    具体来说,用 (rt_i) 维护所有 (pre_jle i)(j),权值线段树表示下标区间。这么一想好像也可以用树套树?最后查询就只用在 (rt_{l-1}) 中找 ([l,r]) 即可。

    另:如果想用 ( m st) 表维护小于 (sqrt {a_i}) 的质数的指数,最好把它开成 char 类型的。

    时间复杂度 (mathcal O(nlog n+86cdot nlog n)),空间复杂度大概在 ( ext S(nlog n+86cdot 4n))

    我会分块!

    预处理整块之间的 ( m lcm)。对于散块,预处理 (pre,nxt),当落在区间之外才计算贡献。

    时间复杂度 (mathcal O(nsqrt n+86cdot nlog n)),空间复杂度 ( ext S(n+86cdot 4n))

    我会归并!

    ((pre_i,a_i)) 放在线段树的叶子节点上,再按 (pre_i) 的大小往上归并。因为一个点只会被归并 (log n) 次,所以复杂度有保障。查询的时候,将 ((l,r)) 划分成线段树上的节点,在节点的归并序列中二分最后一个 (pre<l) 的位置 (p),这个节点的贡献就是到 (p) 的前缀积。

    时间复杂度 (mathcal O(q log^2 n+86cdot nlog n)),空间复杂度 ( ext S(nlog n+86cdot 4n))

    代码

    代码写的是主席树,是我觉得最经济的方案。

    #include <cstdio>
    #define print(x,y) write(x),putchar(y)
    
    template <class T>
    inline T read(const T sample) {
    	T x=0; char s; bool f=0;
    	while((s=getchar())>'9' or s<'0')
    		f|=(s=='-');
    	while(s>='0' and s<='9')
    		x=(x<<1)+(x<<3)+(s^48),
    		s=getchar();
    	return f?-x:x;
    }
    
    template <class T>
    inline void write(const T x) {
    	if(x<0) {
    		putchar('-'),write(-x);
    		return;
    	}
    	if(x>9) write(x/10);
    	putchar(x%10^48);
    }
    
    #include <iostream>
    using namespace std;
    
    const int mod=1e9+7,maxn=1e5+5,lim=2e5;
    
    int n,t[maxn<<2][86],a[maxn];
    int p[100],pc=-1,rt[maxn],idx;
    int nxt[maxn],val[lim+5],Q[86];
    int P[86][19];
    bool vis[500];
    struct node {
    	int ls,rs,mul;
    } s[maxn*90];
    
    void build(int o,int l,int r) {
    	if(l==r) {
    		a[l]=read(9);
    		for(int i=0;i<=pc;++i)
    			if(a[l]%p[i]==0) {
    				while(a[l]%p[i]==0)
    					++t[o][i],
    					a[l]/=p[i];
    			}
    		return;
    	}
    	int mid=l+r>>1;
    	build(o<<1,l,mid);
    	build(o<<1|1,mid+1,r);
    	for(int i=0;i<=pc;++i)
    		t[o][i]=max(t[o<<1][i],t[o<<1|1][i]);
    }
    
    void Query(int o,int l,int r,int L,int R) {
    	if(l>R or r<L) return;
    	if(l>=L and r<=R) {
    		for(int i=0;i<=pc;++i)
    			Q[i]=max(Q[i],t[o][i]);
    		return;
    	}
    	int mid=l+r>>1;
    	Query(o<<1,l,mid,L,R);
    	Query(o<<1|1,mid+1,r,L,R); 
    }
    
    void sieve() {
    	for(int i=2;i<=447;++i) {
    		if(!vis[i]) p[++pc]=i;
    		for(int j=0;j<=pc and i*p[j]<=447;++j) {
    			vis[i*p[j]]=1;
    			if(i%p[j]==0) break;
    		}
    	}
    }
    
    void Build(int &o,int l,int r) {
    	s[o=++idx].mul=1;
    	if(l==r) return;
    	int mid=l+r>>1;
    	Build(s[o].ls,l,mid);
    	Build(s[o].rs,mid+1,r); 
    }
    
    void ins(int o,int l,int r,int p,int k) {
    	if(l==r) return (void)(s[o].mul=k);
    	int mid=l+r>>1;
    	if(p<=mid) ins(s[o].ls,l,mid,p,k);
    	else ins(s[o].rs,mid+1,r,p,k);
    	s[o].mul=1ll*s[s[o].ls].mul*s[s[o].rs].mul%mod;
    }
    
    void modify(int &o,int fa,int l,int r,int p,int k) {
    	o=++idx; s[o]=s[fa];
    	if(l==r) return (void)(s[o].mul=k);
    	int mid=l+r>>1;
    	if(p<=mid) modify(s[o].ls,s[fa].ls,l,mid,p,k);
    	else modify(s[o].rs,s[fa].rs,mid+1,r,p,k);
    	s[o].mul=1ll*s[s[o].ls].mul*s[s[o].rs].mul%mod;
    }
    
    int ask(int o,int l,int r,int L,int R) {
    	if(l>R or r<L) return 1;
    	if(l>=L and r<=R) return s[o].mul;
    	int mid=l+r>>1;
    	return 1ll*ask(s[o].ls,l,mid,L,R)*
    			ask(s[o].rs,mid+1,r,L,R)%mod;
    }
    
    void init_SgTree() {
    	Build(rt[0],1,n);
    	for(int i=1;i<=n;++i) {
    		if(!val[a[i]])
    			ins(rt[0],1,n,i,a[i]);
    		else nxt[val[a[i]]]=i;
    		val[a[i]]=i;
    	}
    	for(int i=1;i<=n;++i)
    		if(nxt[i])
    			modify(rt[i],rt[i-1],1,n,nxt[i],a[i]);
    		else rt[i]=rt[i-1];
    }
    
    void init_pow() {
    	for(int i=0;i<=pc;++i) {
    		P[i][0]=1;
    		for(int j=1;j<=18;++j)
    			P[i][j]=1ll*P[i][j-1]*p[i]%mod;
    	}
    }
    
    int main() {
    	sieve();
    	n=read(9);
    	build(1,1,n);
    	int lst=0;
    	init_pow();
    	init_SgTree();
    	for(int q=read(9);q;--q) {
    		int l,r;
    		l=(read(9)+lst)%n+1;
    		r=(read(9)+lst)%n+1;
    		if(l>r) swap(l,r);
    		Query(1,1,n,l,r);
    		int ans=1;
    		for(int i=0;i<=pc;++i)
    			ans=1ll*ans*P[i][Q[i]]%mod,
    			Q[i]=0;
    		ans=1ll*ans*ask(rt[l-1],1,n,l,r)%mod;
    		print(lst=ans,'
    ');
    	}
    	return 0;
    }
    

    ( ext{Colorful Squares})

    解法

    首先二分长度 (L)。从小到大枚举 (x),考虑 ([max{1,x-L},x]) 中的点,如果满足 (y) 坐标存在一段区间,里面包含的颜色种数 (ge k)(L) 就是可行的。

    一段 (y) 坐标中不同的颜色数可以用线段树维护,也即区间加,区间 (max)。关键是如何计算一个点统治的区间。可以对每种颜色开一个 multiset,查询离当前点最近的两个 (y) 值的统治区间,不让它们产生交即可。具体实现可以看代码。

    总时间复杂度 (mathcal O(nlog^2 n))。这里视 (n) 与值域相等。

    代码

    #include <cstdio>
    #define print(x,y) write(x),putchar(y)
    
    template <class T>
    inline T read(const T sample) {
    	T x=0; char s; bool f=0;
    	while((s=getchar())>'9' or s<'0')
    		f|=(s=='-');
    	while(s>='0' and s<='9')
    		x=(x<<1)+(x<<3)+(s^48),
    		s=getchar();
    	return f?-x:x;
    }
    
    template <class T>
    inline void write(const T x) {
    	if(x<0) {
    		putchar('-'),write(-x);
    		return;
    	}
    	if(x>9) write(x/10);
    	putchar(x%10^48);
    }
    
    #include <bits/stdc++.h>
    using namespace std;
    
    const int maxs=1e5+5,maxn=250000;
    
    int n,k;
    multiset <int> s[maxn+5];
    multiset <int> :: iterator it;
    struct SgTree {
    	struct node {
    		int la,v;
    	} t[maxn+5<<2];
    	
    	void build(int o,int l,int r) {
    		t[o].la=t[o].v=0;
    		if(l==r) return;
    		int mid=l+r>>1;
    		build(o<<1,l,mid);
    		build(o<<1|1,mid+1,r);
    	}
    	
    	void pushDown(int o) {
    		if(!t[o].la) return;
    		t[o<<1].v+=t[o].la;
    		t[o<<1|1].v+=t[o].la;
    		t[o<<1].la+=t[o].la;
    		t[o<<1|1].la+=t[o].la;
    		t[o].la=0;
    	}
    	
    	void modify(int o,int l,int r,int L,int R,int k) {
    		if(L>R or l>R or r<L) return;
    		if(l>=L and r<=R) {
    			t[o].v+=k;
    			t[o].la+=k;
    			return;
    		}
    		pushDown(o);
    		int mid=l+r>>1;
    		modify(o<<1,l,mid,L,R,k);
    		modify(o<<1|1,mid+1,r,L,R,k);
    		t[o].v=max(t[o<<1].v,t[o<<1|1].v);
    	}
    } T;
    struct pii {
    	int y,c;
    };
    vector <pii> Y[maxn+5];
    
    bool ok(int mid) {
    	T.build(1,1,maxn);
    	for(int i=1;i<=k;++i)
    		s[i].clear();
    	for(int i=1;i<=maxn;++i) {
    		for(pii j:Y[i]) {
    			it=s[j.c].find(j.y);
    			if(it!=s[j.c].end()) {
    				s[j.c].insert(j.y);
    				continue;
    			}
    			s[j.c].insert(j.y);
    			it=s[j.c].lower_bound(j.y);
    			int l,r;
    			if(it==s[j.c].begin())
    				l=max(1,j.y-mid);
    			else {
    				--it;
    				l=max(j.y-mid,*it+1);
    			}
    			it=s[j.c].upper_bound(j.y);
    			if(it==s[j.c].end()) r=j.y;
    			else r=min(j.y,*it-mid-1);
    			T.modify(1,1,maxn,l,r,1);
    		}
    		if(i-mid-1>=1) {
    			for(pii j:Y[i-mid-1]) {
    				s[j.c].erase(s[j.c].find(j.y));
    				it=s[j.c].find(j.y);
    				if(it!=s[j.c].end()) 
    					continue;
    				it=s[j.c].lower_bound(j.y);
    				int l,r;
    				if(it==s[j.c].begin())
    					l=max(1,j.y-mid);
    				else {
    					--it;
    					l=max(j.y-mid,*it+1);
    				}
    				it=s[j.c].upper_bound(j.y);
    				if(it==s[j.c].end()) r=j.y;
    				else r=min(j.y,*it-mid-1);
    				T.modify(1,1,maxn,l,r,-1);
    			}
    		}
    		if(T.t[1].v>=k) return 1;
    	}
    	return 0;
    }
    
    int main() {
    	n=read(9),k=read(9);
    	for(int i=1;i<=n;++i) {
    		int x,y,z;
    		x=read(9),y=read(9),z=read(9);
    		Y[x].push_back((pii){y,z});
    	}
    	int l=0,r=maxn,mid;
    	while(l<r) {
    		int mid=l+r>>1;
    		if(ok(mid)) r=mid;
    		else l=mid+1;
    	}
    	print(l,'
    ');
    	return 0;
    }
    
  • 相关阅读:
    FullCalendar日历插件说明文档
    Git忽略规则及.gitignore规则不生效的解决办法
    学习git config配置文件
    拼接最长回文串
    Gildong owns a bulgogi restaurant
    前m大的数
    求水洼(dfs)
    循环数组的最大子段和
    求叶子节点
    周期
  • 原文地址:https://www.cnblogs.com/AWhiteWall/p/15211621.html
Copyright © 2020-2023  润新知