部分CF题目汇总

Posted on 2020-11-02 19:53  Choimoe  阅读(9)  评论(0编辑  收藏  举报

CF1168B 构造+暴力

题意:给定一个长度为 \(n\)\(01\)\(s\),询问有多少个区间 \([l,r]\) 满足:\(\exists x,y\in[l,r]\)\(s_x,s_y,s_{x+y\over 2}\) 的值相同。

题解:直接暴力,利用下面这个结论可以说明复杂度是正确的。

结论:不存在长度超过 \(9\) 的不合法区间。

证明:

这里只证明对于一个无限长的串,保证存在相同值的两点及其中点。

考虑反证。取两个颜色相同的点 \(A,B\),不失一般性地令 \(A\)\(B\) 对应的值为 \(1\)。对于其中点 \(C\),若不存在,则 \(C\) 的值为 \(0\)

再考虑两点 \(C,D\),满足 \(|CA|=|AB|=|BD|\),现在考虑 \(C\) 的值:

  1. \(C=1\) 时,由于 \(A=1,B=1\),于是存在,矛盾。
  2. \(C=0\) 时,不难发现 \(D=0\),与 \(CD\) 中点组成了相同值的三点,矛盾。

综上,必定存在两点及其中点值相同。

代码很普通,就不放了

CF1391C 构造+找规律

题目大意:

给定一个长度为n的排列,求有多少种排列使得经过下列操作后图中存在环。操作的内容是一个排列中第i个位置向两边第一个比它大的位置连边。

题解:

观察性质,首先发现对于一个单调递增的序列和单调递减的序列一定不存在环(因为第i个点只连向下一个点)。

但是这样根据样例会发现会少算很多情况。手玩n=3的情况会发现1 3 2或者是2 3 1也是满足的,也就是对于单峰的序列都可以这样做。

这样的答案如何统计呢,不难发现,可以枚举最高点n所在的位置,剩下的数要考虑放在左边还是放在右边。稍微推了几个情况会发现,只要确定了剩下的数字的放置情况,就可以确定最高点的位置,注意到每个点有放在左边和放在右边两种情况,于是不存在环的情况共有2的n-1次方个。

答案就是n!-2^(n-1)。

CF706D trie树+贪心

题意:维护一个可重集,支持插入删除,以及给定x查询最大异或值

题解:垃圾trie题,不知道为什么写的很像线段树。。

#define N 200005
#define L 30

struct No{int c[2],s,v;}t[N<<5];

int n,rt,c,g;

void upd(int o){t[o].s=t[t[o].c[0]].s+t[t[o].c[1]].s;}
void ins(int&o,int x,int d,int b=L-1){
	if(!o)o=++c;
	if(b==-1){t[o].s+=d;t[o].v+=d;return;}
	ins(t[o].c[(x>>b)&1],x,d,b-1);upd(o);
}
int que(int&o,int x,int d=0,int R=0,int b=L-1){
	if(b==-1)return R;
	if(t[t[o].c[!((x>>b)&1)]].s)return std::max(R,que(t[o].c[!((x>>b)&1)],x,1,R+(1<<b),b-1));
	else return std::max(R,que(t[o].c[(x>>b)&1],x,0,R,b-1));
}

signed main(){
	n=rd();
	ins(rt,0,1);
	jk(i,1,n){
		g=getchar();
		if(g=='?')P(que(rt,rd())),putchar('\n');
		else ins(rt,rd(),g=='+'?1:-1);
	}
}

CF61E 离散化+树状数组

题意:给定序列 \(\{a_i\},i=1...n\),求三元组 \((i,j,k)\) 满足 \(i<j<k\)\(a_i>a_j>a_k\) 的个数,保证 \(\forall i, j\in[1,n]\)\(i\neq j\),有 \(a_i\neq a_j\)

题解:枚举中间点,相当于询问两边有多少点比它高/低,直接离散化然后建两个树状数组维护权值就好了。

#define N 1000010
#define lo(x) x&(-x)

int n,a[N],b[N],r;

struct BIT{
	int t[N];
	void ad(int x,int d){for(;x<=n;x+=lo(x))t[x]+=d;}
	int qu(int x){int r=0;for(;x>=1;x-=lo(x))r+=t[x];return r;}
}L,R;

signed main(){
	n=rd();jk(i,1,n)a[i]=b[i]=rd();
	std::sort(b+1,b+n+1);
	jk(i,1,n)a[i]=std::lower_bound(b+1,b+n+1,a[i])-b;
	jk(i,1,n)R.ad(a[i],1);
	jk(i,1,n){R.ad(a[i],-1);r+=R.qu(a[i])*(i-1-L.qu(a[i]));L.ad(a[i],1);}
	P(r);
}

CF1354D 树状数组上二分

题意:给定一个不降序列 \(\{a_i\}\),需要支持插入一个数,删除第k大的数。卡空间,每个测试点28MB。

题解:直接树状数组上二分。

二分的代码,需要观察到和的一些性质

int kt(int x){
	int R=0,c=0;
	kj(i,20,0)if(R+(1<<i)<n&&c+t[R+(1<<i)]<x)R+=1<<i,c+=t[R];
	return R+1;
}

完整代码:

#define N 1000005
#define lo(x) x&-x
 
int n,t[N+5],q,p,z;
void ad(int x,int d){for(;x<=n;x+=lo(x))t[x]+=d;}
int qu(int x){int r=0;for(;x;x-=lo(x))r+=t[x];return r;}
int kt(int x){int R=0,c=0;kj(i,20,0)if(R+(1<<i)<n&&c+t[R+(1<<i)]<x)R+=1<<i,c+=t[R];return R+1;}
 
signed main(){
	z=n=rd();q=rd();jk(i,1,n)ad(rd(),1);
	while(q--){p=rd();ad(p>0?p:kt(-p),(p>0)-(p<0));z+=(p>0)-(p<0);}
	P(z?kt(z):0);
}