AFO

牛客网NOIP赛前集训营-提高组(第一场)

T1
链接:A

小N得到了一个非常神奇的序列A。这个序列长度为N,下标从1开始。A的一个子区间对应一个序列,可以由数对[l,r]表示,代表A[l], A[l + 1], ..., A[r]这段数。对于一个序列B[1], B[2], ..., B[k],定义B的中位数如下:

  1. 先对B排序。得到新的序列C。

  2. 假如k是奇数,那么中位数为。假如k为偶数,中位数为

对于A的所有的子区间,小N可以知道它们对应的中位数。现在小N想知道,所有长度>=Len的子区间中,中位数最大可以是多少。


一开始打算打60分的平衡树偏分来着...突然想到似乎可以二分?

由于中位数大小其实并不具有单调性(比如最小数在k>1的情况下无论如何也不会是中位数..)但是可行性还是单调的,每次check一个二分到的值\(x\)时把所有大于等于\(x\)的数全部赋值为1,小于\(x\)的数全部赋值为\(-1\)维护一个前缀和然后穷举每一个区间右端点然后找到长度大于等于k的最小值左端点,若相减只差大于0则最终答案肯定在\(x\)\(x\)以上,否则则在\(x\)以下。

考试时写的愚蠢代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define update(now) c[now]=min(c[now*2],c[now*2+1])
using namespace std;

int i,m,j,k,a[100001],t[100001],pre[100001],maxx,c[500001],d[100001],e[100001],n,g[100001],w[100001];

void built(int now,int l,int r)
{
	if(l==r) {c[now]=e[l]; return ;}
	int mid=(l+r)>>1;
	built(now*2,l,mid);
	built(now*2+1,mid+1,r);
	update(now);
}

int cha(int now,int l,int r,int rr)
{
	int ans=0x3f3f3f3f;
	if(r<=rr) return c[now];
	int mid=(l+r)/2;
	if(mid+1<=rr)  ans=min(ans,cha(now*2+1,mid+1,r,rr));
	ans=min(ans,cha(now*2,l,mid,rr));
	return ans;
}

bool pan(int x)
{
	memset(c,0x3f,sizeof(c));
	memset(g,0,sizeof(g));
	memset(w,0,sizeof(w));
	e[0]=0;
	
	for(int i=1;i<=n;i++) 
	{
		if(a[i]>x) d[i]=1,g[i]=g[i-1]; 
		else if(a[i]==x) d[i]=0, g[i]=i;
		else d[i]=-1,g[i]=g[i-1];
		e[i]=e[i-1]+d[i];
	}
	
	built(1,0,n);
	for(int i=k;i<=n;i++)
		if(e[i]-cha(1,0,n,i-k+1)>0) return 1;
	for(int i=k;i<=n;i++)
	if(g[i]) if(e[i]-cha(1,0,n,min(i-k,g[i]))>=0) return 1;
	return 0;
}


int ef(int l,int r)
{
	while(l<r) 
	{
		int mid=(l+r+1)/2;
		if(mid==36)
		{
			mid=36;
		}
		if(pan(mid)) l=mid;
		else r=mid-1;
	}
	return l;
}

int main()
{
	scanf("%d%d",&n,&k);
	for(i=1;i<=n;i++) scanf("%d",&a[i]),t[i]=a[i];
	sort(t+1,t+n+1);

	m=unique(t+1,t+1+n)-t-1;
	for(int i=1;i<=n;i++) 
	{
		int z=lower_bound(t+1,t+1+m,a[i])-t;
		pre[z]=a[i];	a[i]=z;
		maxx=max(maxx,a[i]);
	}
	printf("%d",pre[ef(1,maxx)]);
}

然鹅不用啊,最小值是单调的....
修改后

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;

int i,m,j,k,a[100001],maxx,c[500001],d[100001],e[100001],n,tmp;

bool pan(int x)
{
	for(int i=1;i<=n;i++) 
	{
		if(a[i]>=x) e[i]=e[i-1]+1; 
		else e[i]=e[i-1]-1;
	}
	int p=0x3f3f3f3f;
	for(int i=k;i<=n;i++)
	{
		p=min(p,e[i-k]);
		if(e[i]-p>0) return 1;
	}
	return 0;
}

int main()
{
	scanf("%d%d",&n,&k);
	for(i=1;i<=n;i++) scanf("%d",&a[i]),maxx=max(maxx,a[i]);
	int l=1, r=maxx;
	while(l<=r) 
	{
		int mid=(l+r)/2;
		if(pan(mid)) tmp=mid,l=mid+1;
		else r=mid-1;
	}
	printf("%d",tmp);
}

T2
链接:B

小N对于数字的大小一直都有两种看法。第一种看法是,使用字典序的大小(也就是我们常用的判断数字大小的方法,假如比较的数字长度不同,则在较短一个前面补齐前导0,再比较字典序),比如43<355,10<11。第二种看法是,对于一个数字,定义他的权值为,也就是各个数位的乘积。

现在给定两个区间,[L,R]与[L1,R1]。小N现在想知道,有多少使用字典序判大小法在[L,R]之间的数字,满足其第二种定义的权值也在[L1,R1]之间。

换句话说,对于一个数x,定义f(x)为x的各个数位的乘积。对于L<=x<=R,问有多少x满足,L1<=f(x)<=R1。


恶意拉长题目描述差评

45分暴力很好啊QWQ

正解
数位dp,把情况分为两种,包含0和不包含0,然后包含0为至少含有1个0的情况,特判即可。
不包含0
\(L1,R1\)质因数分解,由于每一位都\(\in[x|9\leq x\leq1]\)所以分解出的质因数也只有\(2,3,5,7\)四个且都不会超过60个。然后就是数位dp


T3
链接:C
来源:牛客网

C国有n个城市,城市间通过一个树形结构形成一个连通图。城市编号为1到n,其中1号城市为首都。国家有m支军队,分别守卫一条路径的城市。具体来说,对于军队i,他守卫的城市区域可以由一对二元组(xi,yi)代表。表示对于所有在xi到yi的最短路径上的城市,军队i都会守卫他们。
现在有q个重要人物。对于一个重要人物j,他要从他的辖区vj出发,去到首都。出于某些原因,他希望找到一个离首都最近的,且在vj到首都路径上的城市uj,使得至少有kj支军队,能够全程保护他从vj到uj上所经过的所有城市。换句话说,至少有ki支军队,满足在树上,xi到yi的路径能完全覆盖掉vj到uj的路径。


一看就是万恶的数据结构题...(咕咕咕


posted @ 2019-10-12 16:04  ZUTTER☮  阅读(168)  评论(0编辑  收藏  举报