7.20考试总结(NOIP模拟21)[Median·Game·Park]

雨滴降落的速度是每秒十米,我该用怎么样的速度,才能将你挽留?

前言

关于语文素养如何限制OI水平2,正好现在文化课巨佬们正在考语文(那我走???)

T1 我以为整数是不用输出 .0 的,然后喜挂 30pts。

T2 差值不应该是绝对值???,然后题解认为不需要取,喜挂 45pts。

T1 Median

解题思路

首先明确一下暴力分(线性筛+暴力排序),有 50pts 之高。

其次明确一下中位数需要排序,然后输出方面上面也说了。。

正解有一点玄学,官方题解是这么说的:

然后注意到这题的输入很诡异,其实可以当做是随机数据

那么随机数据满足分布均匀,也就是说可以假定,中位数值变化也是常数级的。

然后题解就认为可以暴力维护中位数指针了。。。(我***)

对于奇数而言,先暴力排序搞出第一个区间的中位数以及各个数的桶,并且记录一下小于等于中位数的数的数量。

然后在向后查找的时候,不断维护更新中位数以及桶还有数量就好了。

偶数的话,维护两个要取平均值的两个数就好了。

Linux编译的时候终端对于大的数据可能会直接判断为 RE,可以开 O2 或者 Ofast:

g++ t.cpp -Ofast -o t && ./t

然后处理的时候注意一下边界问题,并且不要全部开 long long 就 OK 了。

code

#include<bits/stdc++.h>
//#define int long long
#define re long long
#define f() cout<<"Pass"<<endl;
using namespace std;
inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch>'9'||ch<'0')
	{
		if(ch=='-')	f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*f;
}
const int N=1e7+10;
int n,len,mod,num,num1,num2,cnt,pri[N],s[N],s2[N];
int sta[N];
int sum,sum1,sum2,vis[N];
bitset<18*N> jud;
double ans;
void Get_Prime()
{
	for(int i=2;cnt<n;i++)
	{
		if(!jud[i])	pri[++cnt]=i;
		for(int j=1;j<=cnt&&pri[j]*i<=18*n;j++){
			jud[pri[j]*i]=true;
			if(i%pri[j]==0)break;
		}
	}
}
signed main()
{
	n=read();
	len=read();
	mod=read();
	Get_Prime();
	for(int i=1;i<=n;i++)
		s[i]=1ll*i*pri[i]%mod;
	for(int i=1;i<=n;i++)
		s2[i]=s[i]+s[i/10+1];
	for(int i=1;i<=len;i++)
		sta[i]=s2[i],vis[s2[i]]++;
	sort(sta+1,sta+len+1);
	num=len/2;
	if(len&1)	ans+=sta[num+1]*1.0;
	else	ans+=(1.0*sta[num]+1.0*sta[num+1])/2.0;
	num=sta[len/2+1];
	num1=sta[len/2],num2=sta[len/2+1];
	if(len&1)
		for(int i=0;i<=num;i++)
			sum+=vis[i];
	else
	{
		for(int i=0;i<=num2;i++)
			sum1+=vis[i];
		for(int i=0;i<=num1;i++)
			sum2+=vis[i];
	}
	for(int l=2;l<=n-len+1;l++)
	{
		int r=l+len-1;
		if(len&1)
		{
			vis[s2[l-1]]--;
			if(s2[l-1]<=num)	sum--;
			vis[s2[r]]++;
			if(s2[r]<=num)	sum++;
			while(1)
			{			
				if(sum-vis[num]>len/2)	sum-=vis[num],num--;
				else	if(len-sum>len/2)	num++,sum+=vis[num];
				if(sum-vis[num]<=len/2&&len-sum<=len/2)
					break;
			}
			ans+=num;
		}
		else
		{
			vis[s2[l-1]]--;
			vis[s2[r]]++;
			if(s2[l-1]<=num1)	sum1--;
			if(s2[r]<=num1)	sum1++;
			if(s2[l-1]<=num2)	sum2--;
			if(s2[r]<=num2)	sum2++;
			while(sum1-vis[num1]>=len/2)	sum1-=vis[num1],num1--;
			while(sum1+vis[num1+1]<len/2)	sum1+=vis[num1+1],num1++;
			if(sum1<len/2)	num1++,sum1+=vis[num1];
			while(sum2-vis[num2]>=len/2+1)	sum2-=vis[num2],num2--;
			while(sum2+vis[num2+1]<len/2+1)	sum2+=vis[num2+1],num2++;
			if(sum2<len/2+1)	num2++,sum2+=vis[num2];
			ans+=(1.0*num1+1.0*num2)/2.0;
		}
	}
	printf("%.1lf",ans);
	return 0;
}

T2 Game

解题思路

对于非正解的算法而言,set 或者是优先队列都可以搞到 50pts。

其实并不需要那么麻烦的操作,不难发现,如果要加入的数大于当前区间的最大值的话,那么它就一定会被取走。

因此只需要维护新进入区间的值就可以了。

然后观察到其实值域很小,因此可以开个桶维护。

code

#include<bits/stdc++.h>
#define int long long
#define f() cout<<"Pass"<<endl;
using namespace std;
inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch>'9'||ch<'0')
	{
		if(ch=='-')	f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*f;
}
const int N=1e5+10,m=1e7+10;
int n,opt,now,maxn,ans[2],T,s[N],t[N];
void solve()
{
	opt=read();
	ans[0]=ans[1]=0;
	maxn=now=0;
	for(int i=1;i<opt;i++)
	{
		t[s[i]]++;
		maxn=max(maxn,s[i]);
	}
	for(int i=opt;i<=n;i++)
	{
		if(s[i]>=maxn)	ans[now]+=s[i];
		else
		{
			t[s[i]]++;
			t[maxn]--;
			ans[now]+=maxn;
			while(!t[maxn])	maxn--;
		}
		now=(!now);
	}
	for(int i=maxn;i>=1;i--)
		while(t[i])
		{
			ans[now]+=i;
			t[i]--;
			now=(!now);
		}
	printf("%lld\n",ans[0]-ans[1]);
}
signed main()
{
	n=read();
	T=read();
	for(int i=1;i<=n;i++)
		s[i]=read();
	while(T--)	solve();
	return 0;
}

T3 Park

解题思路

出题人对于[CEOI2017]Chase 的题面魔改了一下就扔给了我们。

树形 DP,开两个数组 f[i][j]g[i][j] 分别记录从 i 到 i 的子树以及从 i 的子树到 i 撒了 j 个的最大贡献。

然后尝试着把在一棵子树中经过子树根节点一条路径(假设是根节点为 rt 从 \(u\rightarrow v\))拆成两部分:

\(u\rightarrow rt\rightarrow v\) 这样就可以用 f 和 g 数组进行转移了。

然后预处理出每个节点周围节点的值的和,在运算的时候减去已经经过的。

最后要注意,对于每一棵子树要正反进行两边,要保证每一种解都可以扫到,毕竟路径的正反是不同的。

code

#include<bits/stdc++.h>
#define int long long
#define f() cout<<"Pass"<<endl;
using namespace std;
inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch>'9'||ch<'0')
	{
		if(ch=='-')	f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*f;
}
const int N=1e5+10;
int n,m,ans,s[N],cnt[N],f[N][110],g[N][110];
int tot,head[N],nxt[N<<1],ver[N<<1];
void add_edge(int x,int y)
{
	ver[++tot]=y;
	nxt[tot]=head[x];
	head[x]=tot;
}
void dfs(int x,int fa)
{
	vector<int> v;
	vector<int>().swap(v);
	for(int i=1;i<=m;i++)
	{
		f[x][i]=cnt[x];
		g[x][i]=cnt[x]-s[fa];
	}
	for(int i=head[x];i;i=nxt[i])
	{
		int to=ver[i];
		if(to==fa)	continue;
		v.push_back(to);
		dfs(to,x);
		for(int j=0;j<=m;j++)
			ans=max(ans,f[x][j]+g[to][m-j]);
		for(int j=1;j<=m;j++)
		{
			f[x][j]=max(f[x][j],max(f[to][j],f[to][j-1]+cnt[x]-s[to]));
			g[x][j]=max(g[x][j],max(g[to][j],g[to][j-1]+cnt[x]-s[fa]));
		}
	}
	for(int i=1;i<=m;i++)
	{
		f[x][i]=cnt[x];
		g[x][i]=cnt[x]-s[fa];
	}
	if(!v.size())	return ;
	for(int i=v.size()-1;i>=0;i--)
	{
		int to=v[i];
		for(int j=0;j<=m;j++)
			ans=max(ans,f[x][j]+g[to][m-j]);
		for(int j=1;j<=m;j++)
		{
			f[x][j]=max(f[x][j],max(f[to][j],f[to][j-1]+cnt[x]-s[to]));
			g[x][j]=max(g[x][j],max(g[to][j],g[to][j-1]+cnt[x]-s[fa]));
		}
	}
}
signed main()
{
	n=read();
	m=read();
	for(int i=1;i<=n;i++)
		s[i]=read();
	for(int i=1,x,y;i<n;i++)
	{
		x=read();
		y=read();
		add_edge(x,y);
		add_edge(y,x);
		cnt[x]+=s[y];
		cnt[y]+=s[x];
	}
	dfs(1,0);
	printf("%lld",ans);
	return 0;
}
posted @ 2021-07-21 10:45  Varuxn  阅读(90)  评论(0编辑  收藏  举报