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)\)的大小关系即可。