Codeforces Round #852 div2

CF852div2
F
Q个询问 问数列中的一段区间内任意两个数相差的最小值.

考虑固定右端点查每个左端点的答案 做主席树的话不支持相减性。

考虑线段树上直接维护以每个左端点i为起点的答案 观察移动右端点j时的答案变化。

用aj更新每一个\(ai\) 不妨设 \(aj>ai\) 直接一个一个更新复杂度还是\(O(n)\)的。

此时可以看做是一个归纳的过程:若aj更新了ai 考虑aj将要更新的\(ak(k<i)\)

如果有 \(aj>ai>ak\) 那么aj的更新没有意义。

如果有 \(aj>ak>ai\) 当且仅当 \(aj-ak<ak-ai\)时才有意义。不等式两边同时加上\(aj-ak\)

\(2(aj-ak)<aj-ai\) 可以发现差值每次/2 这样最多更新log次

需要做的操作是每次找到一个离j最近的ai 满足\(aj>ai\)且差值小于上次差值/2.

aj<ai的时候同理。正确性用到了归纳法。

利用主席树可以很容易做到复杂度\(nlog^2+Qlog\)

code
// LUOGU_RID: 102214066
//#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<ctime>
#include<cctype>
#include<queue>
#include<deque>
#include<stack>
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<cctype>
#include<cstdlib>
#include<queue>
#include<deque>
#include<stack>
#include<vector>
#include<algorithm>
#include<utility>
#include<bitset>
#include<set>
#include<map>
#define ll long long
#define db double
#define INF 1000000000
#define inf 100000000000000000ll
#define ldb long double
#define pb push_back
#define put_(x) printf("%d ",x);
#define putl_(x) printf("%lld ",x);
#define get(x) x=read()
#define putl(x) printf("%lld\n",x)
#define rep(p,n,i) for(int i=p;i<=n;++i)
#define go(x) for(int i=lin[x],tn=ver[i];i;tn=ver[i=nex[i]])
#define pii pair<int,int>
#define mk make_pair
#define gf(x) scanf("%lf",&x)
#define pf(x) ((x)*(x))
#define uint unsigned long long
#define ui unsigned
#define sq sqrt
#define l(w) t[w].l
#define r(w) t[w].r
#define id(w) t[w].id
#define R(w) s[w].r
#define yy p<<1|1
#define zz p<<1
#define sum(w) t[w].sum
#define mod 1000000007
#define sc(A) scanf("%d",&A)
#define scs(A) scanf("%s",A);
#define put(A) printf("%d\n",A)
#define min(x,y) (x>=y?y:x)
#define max(x,y) (x>=y?x:y)
#define sub(x,y) (x-y<0?x-y+mod:x-y)
using namespace std;
const int MAXN=300010;
int n,Q;
int s[MAXN<<2],a[MAXN],ans[MAXN<<2],c[MAXN];
struct wy
{
	int l,r,id;
}t[MAXN<<2];
inline int cmp(wy a,wy b)
{
	return a.r<b.r;
}
inline void change(int p,int l,int r,int x)
{
	if(l==r){s[p]=x;return;}
	int mid=(l+r)>>1;
	if(a[x]<=mid)change(zz,l,mid,x);
	else change(yy,mid+1,r,x);
	s[p]=max(s[zz],s[yy]);
}
inline int find(int p,int l,int r,int L,int R)
{
	if(L<=l&&R>=r)return s[p];
	if(l==r)return l;
	int mid=(l+r)>>1,c1=-1,c2=-1;
	if(L<=mid)c1=find(zz,l,mid,L,R);
	if(R>mid)c2=find(yy,mid+1,r,L,R);
	return max(c1,c2);
}
inline void add(int x,int w)
{
	while(x<=n)
	{
		c[x]=min(c[x],w);
		x+=x&(-x);
	}
}
inline int ask(int x)
{
	int cnt=INF;
	while(x)
	{
		cnt=min(cnt,c[x]);
		x-=x&(-x);
	}
	return cnt;
}
int main()
{
	//freopen("1.in","r",stdin);
	sc(n);sc(Q);
	memset(c,0x3f,sizeof(c));
	memset(s,-1,sizeof(s));
	rep(1,n,i)sc(a[i]);
	rep(1,Q,i)
	{
		sc(l(i));
		sc(r(i));
		id(i)=i;
	}
	sort(t+1,t+1+Q,cmp);
	int cnt=0;
	rep(2,n,i)
	{
		change(1,1,n,i-1);
		int w,cc=INF;
		while((w=find(1,1,n,a[i],a[i]+cc))!=-1)
		{
			add(n-w+1,a[w]-a[i]);
			cc=a[w]-a[i];cc=cc/2;
		}
		cc=INF;
		while((w=find(1,1,n,a[i]-cc,a[i]))!=-1)
		{
			add(n-w+1,a[i]-a[w]);
			cc=a[i]-a[w];cc=cc/2;
		}
		while(cnt+1<=Q&&t[cnt+1].r==i)
		{
			++cnt;
			ans[t[cnt].id]=ask(n-t[cnt].l+1);
		}
	}
	rep(1,Q,i)put(ans[i]);
	return 0;
}

E
题意:很复杂。

刚看完感觉很不可做 实际上也很不可做。关键的性质要想到才能做的题。

先将人的ai进行从小到大排序,接着考虑对于书的数量为x可以快乐的人可以一定是{a}的前缀。

证明:如果第i个人不快乐考虑这群人最后一个快乐的为j 交换两者答案不变依然合法。

之后考虑到分到一起的人可以一定是连续的。

证明:考虑最后一个快乐的人为j考虑j那一组和j所在段不连续的为k将k交换到和j连在一起的那一组w。

显然j>w>k 由上一个结论知道k可以快乐同时由于j是快乐的知道w也是快乐的。

经过这两步的调整我们得到了快乐的人的相当规律的分布。

很自然的得到dp:f[i]表示i个人获得了快乐可以分的最大组。

考虑到第i个人必须快乐 那么至少\(i-ai+1\)~i这些人在一组。

维护一个前缀最大值就能转移。

值得注意的是当\(ai>i\)时可以让1~ai一组 但只有i个人快乐。

这种状态就不能加入上面的前缀最大值里。

之后对于每个fi来说能满足的最大的组数为 \(n-i+fi\) 可以维护后缀最大值。

这里我直接倒序枚举i维护最大组数j暴力赋值了。

code
//#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<ctime>
#include<cctype>
#include<queue>
#include<deque>
#include<stack>
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<cctype>
#include<cstdlib>
#include<queue>
#include<deque>
#include<stack>
#include<vector>
#include<algorithm>
#include<utility>
#include<bitset>
#include<set>
#include<map>
#define ll long long
#define db double
#define INF 1000000000
#define inf 100000000000000000ll
#define ldb long double
#define pb push_back
#define put_(x) printf("%d ",x);
#define putl_(x) printf("%lld ",x);
#define get(x) x=read()
#define putl(x) printf("%lld\n",x)
#define rep(p,n,i) for(int i=p;i<=n;++i)
#define go(x) for(int i=lin[x],tn=ver[i];i;tn=ver[i=nex[i]])
#define pii pair<int,int>
#define mk make_pair
#define gf(x) scanf("%lf",&x)
#define pf(x) ((x)*(x))
#define uint unsigned long long
#define ui unsigned
#define sq sqrt
#define l(w) t[w].l
#define r(w) t[w].r
#define id(w) t[w].id
#define R(w) s[w].r
#define yy p<<1|1
#define zz p<<1
#define sum(w) t[w].sum
#define mod 1000000007
#define sc(A) scanf("%d",&A)
#define scs(A) scanf("%s",A);
#define put(A) printf("%d\n",A)
#define min(x,y) (x>=y?y:x)
#define max(x,y) (x>=y?x:y)
#define sub(x,y) (x-y<0?x-y+mod:x-y)
using namespace std;
const int MAXN=300010,N=MAXN<<1;
int n,Q,cnt,root,len,top,t,m;
int f[MAXN],a[MAXN],c[MAXN],ans[MAXN];
int main()
{
	freopen("1.in","r",stdin);
	sc(n);
	rep(1,n,i)
	{
		sc(a[i]);
	}
	sort(a+1,a+1+n);
	rep(1,n,i)
	{
		if(i>=a[i])
		{
			f[i]=c[i-a[i]]+1;
			c[i]=max(c[i-1],f[i]);
			f[i]=f[i]+n-i;
		}
		else 
		{
			c[i]=c[i-1];
			f[i]=n-a[i]+1;
		}
	}
	int w=0;
	for(int i=n;i>=1;--i)
	{
		if(f[i]<=w)continue;
		for(int j=f[i];j>w;--j)ans[j]=i;
		w=f[i];
	}
	sc(Q);
	rep(1,Q,i){sc(t);put(ans[t]);}
	return 0;
}

D
给出俩排列求出MEX值相同的子区间个数。

如果不是排列那就需要线段树维护左端点的MEX值想办法快速数点。

对于这道题很套路考虑枚举\(MEX\)值x 那么满足x的会形成一个最小区间l,r 考虑形成x+1的区间

可能\(x+1\)在l,r里那么x就不能被计数 不在l,r里 那么会得到左指针的区间和右指针的区间

分别对a,b做这个操作各个区间取交相乘即可。

注意特判\(MEX=0\)的情况。

code
//#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<ctime>
#include<cctype>
#include<queue>
#include<deque>
#include<stack>
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<cctype>
#include<cstdlib>
#include<queue>
#include<deque>
#include<stack>
#include<vector>
#include<algorithm>
#include<utility>
#include<bitset>
#include<set>
#include<map>
#define ll long long
#define db double
#define INF 1000000000
#define inf 100000000000000000ll
#define ldb long double
#define pb push_back
#define put_(x) printf("%d ",x);
#define putl_(x) printf("%lld ",x);
#define get(x) x=read()
#define putl(x) printf("%lld\n",x)
#define rep(p,n,i) for(int i=p;i<=n;++i)
#define go(x) for(int i=lin[x],tn=ver[i];i;tn=ver[i=nex[i]])
#define pii pair<int,int>
#define mk make_pair
#define gf(x) scanf("%lf",&x)
#define pf(x) ((x)*(x))
#define uint unsigned long long
#define ui unsigned
#define sq sqrt
#define l(w) t[w].l
#define r(w) t[w].r
#define id(w) t[w].id
#define R(w) s[w].r
#define yy p<<1|1
#define zz p<<1
#define sum(w) t[w].sum
#define mod 1000000007
#define sc(A) scanf("%d",&A)
#define scs(A) scanf("%s",A);
#define put(A) printf("%d\n",A)
#define min(x,y) (x>=y?y:x)
#define max(x,y) (x>=y?x:y)
#define sub(x,y) (x-y<0?x-y+mod:x-y)
using namespace std;
const int MAXN=300010,N=MAXN<<1;
int n;
int a[MAXN],b[MAXN];
int pa[MAXN],pb[MAXN],va[MAXN],vb[MAXN];
ll ans;
inline int calc(int l,int r,int L,int R)
{
	int LL=max(l,L);
	int RR=min(r,R);
	return LL>RR?0:RR-LL+1;
}
inline void calc1(int a1,int b1,int a2,int b2,int a3,int b3,int a4,int b4)
{
	int w1=calc(a1,b1,a3,b3);
	int w2=calc(a2,b2,a4,b4);
	ans+=(ll)w1*w2;
}
int main()
{
	freopen("1.in","r",stdin);
	sc(n);
	rep(1,n,i){sc(a[i]);pa[a[i]]=i;}
	rep(1,n,i){sc(b[i]);pb[b[i]]=i;}
	ll w1=calc(1,pa[1]-1,1,pb[1]-1);
	ans+=w1*(w1+1)/2;
	w1=calc(1,pa[1]-1,pb[1]+1,n);
	ans+=w1*(w1+1)/2;
	w1=calc(pa[1]+1,n,pb[1]+1,n);
	ans+=w1*(w1+1)/2;
	w1=calc(pa[1]+1,n,1,pb[1]-1);
	ans+=w1*(w1+1)/2;
	int l=pa[1],r=l,aa=1;
	int L=pb[1],R=L,ab=1;
	va[1]=vb[1]=1;
	while(1)
	{
		if(aa==n||ab==n)break;
		if(aa==ab)
		{
			if(pa[aa+1]>r&&pb[ab+1]>R)
				calc1(1,l,r,pa[aa+1]-1,1,L,R,pb[ab+1]-1);
			if(pa[aa+1]>r&&pb[ab+1]<L)
				calc1(1,l,r,pa[aa+1]-1,pb[ab+1]+1,L,R,n);
			if(pa[aa+1]<l&&pb[ab+1]>R)
				calc1(pa[aa+1]+1,l,r,n,1,L,R,pb[ab+1]-1);
			if(pa[aa+1]<l&&pb[ab+1]<L)
				calc1(pa[aa+1]+1,l,r,n,pb[ab+1]+1,L,R,n);
			while(l>pa[aa+1])va[a[--l]]=1;
			while(r<pa[aa+1])va[a[++r]]=1;
			while(va[aa+1])++aa;
			while(L>pb[ab+1])vb[b[--L]]=1;
			while(R<pb[ab+1])vb[b[++R]]=1;
			while(vb[ab+1])++ab;
			continue;
		}
		if(aa<ab)
		{
			while(l>pa[aa+1])va[a[--l]]=1;
			while(r<pa[aa+1])va[a[++r]]=1;
			while(va[aa+1])++aa;
			continue;
		}
		if(aa>ab)
		{
			while(L>pb[ab+1])vb[b[--L]]=1;
			while(R<pb[ab+1])vb[b[++R]]=1;
			while(vb[ab+1])++ab;
			continue;
		}
	}
	++ans;
	putl(ans);
	return 0;
}

C
给出一个排列 求出一组\(l,r\)满足\(al,ar\)均不为\([l,r]\)区间内的最大值与最小值。

对于i考虑求出\(Ri\)表示以i为左端点可以合法的最小右端点,显然这个右端点之后的点也是合法的。

同理求出\(Li\)表示以i为右端点可以合法的最大左端点,显然这个左端点之前的点也是合法的。

枚举i 只需在\(1-Li\)中找一个\(j\)满足\(Rj<=i\)即可。

前面两个数组可以使用单调栈后者维护一个前缀最小值即可。

另外一种做法:考虑维护两个指针\(L=1,R=n\)(初始)

之后判断L,R是否合法不断缩小区间即可。正确性:如果存在答案那么必然存在一组\(l,r\)使得包含\(l,r\)这个区间的所有区间都不合法。

使用RMQ求区间最值即可,或者使用set。

code
//#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<ctime>
#include<cctype>
#include<queue>
#include<deque>
#include<stack>
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<cctype>
#include<cstdlib>
#include<queue>
#include<deque>
#include<stack>
#include<vector>
#include<algorithm>
#include<utility>
#include<bitset>
#include<set>
#include<map>
#define ll long long
#define db double
#define INF 1000000000
#define inf 100000000000000000ll
#define ldb long double
#define pb push_back
#define put_(x) printf("%d ",x);
#define putl_(x) printf("%lld ",x);
#define get(x) x=read()
#define putl(x) printf("%lld\n",x)
#define rep(p,n,i) for(int i=p;i<=n;++i)
#define go(x) for(int i=lin[x],tn=ver[i];i;tn=ver[i=nex[i]])
#define pii pair<int,int>
#define mk make_pair
#define gf(x) scanf("%lf",&x)
#define pf(x) ((x)*(x))
#define uint unsigned long long
#define ui unsigned
#define sq sqrt
#define l(w) t[w].l
#define r(w) t[w].r
#define id(w) t[w].id
#define R(w) s[w].r
#define yy p<<1|1
#define zz p<<1
#define sum(w) t[w].sum
#define mod 1000000007
#define sc(A) scanf("%d",&A)
#define scs(A) scanf("%s",A);
#define put(A) printf("%d\n",A)
#define min(x,y) (x>=y?y:x)
#define max(x,y) (x>=y?x:y)
#define sub(x,y) (x-y<0?x-y+mod:x-y)
using namespace std;
const int MAXN=300010,N=MAXN<<1;
int n,T,top,top1;
int a[MAXN],s[MAXN],s1[MAXN],L[MAXN],R[MAXN],c[MAXN],w[MAXN];
int main()
{
	freopen("1.in","r",stdin);
	sc(T);
	while(T--)
	{
		sc(n);
		rep(1,n,i)sc(a[i]);
		top=top1=0;
		for(int i=n;i>=1;--i)
		{
			while(top&&a[s[top]]<a[i])--top;
			if(!top)R[i]=n+1;
			else R[i]=s[top];
			s[++top]=i;
			
			while(top1&&a[s1[top1]]>a[i])--top1;
			if(!top1)R[i]=n+1;
			else R[i]=max(R[i],s1[top1]);
			s1[++top1]=i;
		}
		top=top1=0;
		int flag=0;
		rep(1,n,i)
		{
			while(top&&a[s[top]]<a[i])--top;
			if(!top)L[i]=0;
			else L[i]=s[top];
			s[++top]=i;
			
			while(top1&&a[s1[top1]]>a[i])--top1;
			if(!top1)L[i]=0;
			else L[i]=min(L[i],s1[top1]);
			s1[++top1]=i;
			
			if(L[i]&&c[L[i]]<=i)
			{
				put_(w[L[i]]);
				put(i);
				flag=1;
				break;
			}
			if(i-1>=1&&c[i-1]<=R[i])c[i]=c[i-1],w[i]=w[i-1];
			else c[i]=R[i],w[i]=i;
		}
		if(!flag){puts("-1");}
		rep(1,n,i)L[i]=R[i]=c[i]=w[i]=0;
	}
	return 0;
}

B
构造一个数组满足题意。

容易发现数组长度为\(2(a-b)\)

容易发现一个构造a a-1...b b+1...a-1

A
考虑\(a\cdot m\)\(b\cdot (m+1)\)的大小关系即可。

posted @ 2023-02-17 22:20  chdy  阅读(24)  评论(0编辑  收藏  举报