noip模拟测试21

考试总结:这次考试,前两道题的题面描述不是很清楚,导致我不知道输出格式到底是什么,挂了差不多80分(好多人也是这样),总体来说,这次考试的前两道题暴力分是打满了,最后一道题打了一个假的暴搜,在考场上没调出来,代码能力还需提高。

T1 Median

思路:我们首先利用线性筛求出我们需要的素数,然后求出整个序列,接下来我们考虑计算中位数,首先明确一个问题,这中位数是将区间里的数从大到小排好序之后的中间的那个数,(我当时因为这个调了老半天),之后我们注意到这个区间长度是一个定值,每次移动只会有两个值出现的次数发生变化,那我们就可以利用一个桶,记录每个数出现的次数,然后维护一个指针指向中位数的位置,当我们移动的时候,相应的更新指针的位置,具体实现见代码:

AC_Code
#include<bits/stdc++.h>
#define re register int
#define ii inline int
#define iv inline void
using namespace std;
const int M=2e7+10;
const int G=1e7+10;
const int P=1e8+8e7;
double ans;
int n,k,w,sum,ls;
int s1[M],s2[M],pr[G],u[G],tong[M];
bool ifs[P];
ii read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9')
	{
		if(ch=='-')
			f=0;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return (f)?x:(-x);
}
iv get_prime()
{
	for(re i=2;sum<=G;i++)
	{
		if(!ifs[i])
		{
			pr[++sum]=i;
		}
		for(re j=1;j<=sum&&i*pr[j]<=P;j++)
		{
			ifs[i*pr[j]]=1;
			if(i%pr[j]==0)
				break;
		}
	}
	for(re i=1;i<=n;i++)
		s1[i]=1ll*pr[i]%w*i%w;
	for(re i=1;i<=n;i++)
		s2[i]=1ll*s1[i]+s1[i/10+1];
}
int my(int a,int b)
{
	return a<b;
}
iv solve1()
{
	long long mid;
	for(re i=1;i<=k;i++)
	{
		u[i]=s2[i];
		tong[s2[i]]++;	
	}
	sort(u+1,u+k+1,my);
	mid=u[(k+1)/2];
	ls=upper_bound(u+1,u+k+1,mid)-u-1;
	ans+=mid;
	for(re i=2;i<=n-k+1;i++)
	{
		tong[s2[i-1]]--;
		tong[s2[i+k-1]]++;
		if(s2[i-1]*1ll<=mid)
			ls--;
		if(s2[i+k-1]*1ll<=mid)
			ls++;
		while(1)
		{
			if(1ll*(ls-tong[mid])>=((k+1)/2))
			{
				ls-=tong[mid];
				mid--;
			}
			else if(1ll*(k-ls)>=((k+1)/2))
			{
				mid++;
				ls+=tong[mid];
			}
			if((ls-tong[mid]<((k+1)/2))&&(k-ls<((k+1)/2)))
				break;
		}
		ans+=mid;
	}
	printf("%.1lf\n",(double)ans);
}
iv solve2()
{
	double mid;
	long long l1,l2,m1,m2;
	for(re i=1;i<=k;i++)
	{
		u[i]=s2[i];
		tong[s2[i]]++;	
	}
	sort(u+1,u+k+1,my);
	mid=((double)u[k/2]+(double)u[k/2+1])/(double)2;
	l1=upper_bound(u+1,u+k+1,u[k/2])-u-1;
	l2=upper_bound(u+1,u+k+1,u[k/2+1])-u-1;
	m1=u[k/2];
	m2=u[k/2+1];
	ans+=mid;
	for(re i=2;i<=n-k+1;i++)
	{
		tong[s2[i-1]]--;
		tong[s2[i+k-1]]++;
		if(s2[i-1]<=m1)
		{
			l1--;
		}
		if(s2[i-1]<=m2)
		{
			l2--;
		}
		if(s2[i+k-1]<=m1)
		{
			l1++;	
		}
		if(s2[i+k-1]<=m2)
		{
			l2++;
		}
		while(l1-tong[m1]>=(k/2))
		{
			l1-=tong[m1];
			--m1;
		}
		while(l1+tong[m1+1]<(k/2))
		{
			++m1;
			l1+=tong[m1];
		}
		if(l1<(k/2))
		{
			++m1;
			l1+=tong[m1];
		}
		while(l2-tong[m2]>=(k/2+1))
		{
			l2-=tong[m2];
			m2--;
		}
		while(l2+tong[m2+1]<(k/2+1))
		{
			++m2;
			l2+=tong[m2];
		}
		if(l2<(k/2+1))
		{
			++m2;
			l2+=tong[m2];
		}
		ans+=((double)m1+(double)m2)/(double)2;
	}
	long long out=ans;
	if((ans-(double)out)==0.5)
		printf("%.1lf\n",(double)out+0.5);
	else
		printf("%.1lf\n",(double)out);
	
}
signed main()
{
	n=read();
	k=read();
	w=read();
	get_prime();
	if(k&1)
		solve1();
	else
		solve2();
	/*long long out=ans;
	if((ans-(double)out)==0.5)
		printf("%.1lf\n",(double)out+0.5);
	else
		printf("%.1lf\n",(double)out);*/

	return 0;
}

T2 Game

思路:因为根据最优策略,如果我要放进去的球比当前序列的最大值还要大,那么我直接就把它拿走了,就不把它放入序列了,所以,如果我们维护一个指向当前序列最大值的指针,那么这个指针显然是单调递减的,我们每次操作后维护这个指针即可,具体实现见代码:

AC_Code
#include<bits/stdc++.h>
#define re register int
#define lc rt<<1
#define rc rt<<1|1
#define mid ((l+r)>>1)
#define re register int
#define ii inline int
#define iv inline void
using namespace std;
const int N=1e5+10;
int n,k,p,cnt,num,mx,use;
int a[N],b[N];
long long tong[N];
long long A,B,la,lb;
ii read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9')
	{
		if(ch=='-')
			f=0;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return (f)?x:(-x);
}
iv change()
{
	if(!tong[mx])
	{
		while(1)
		{
			mx--;
			if(tong[mx]||(mx<=0))
				break;
		}
	}
}
int main()
{
	n=read();
	k=read();
	for(re i=1;i<=n;i++)
		a[i]=read();
	for(re i=1;i<=k;i++)
	{
		p=read();
		A=0;
		B=0;
		la=-1;
		lb=-1;
		cnt=0;
		mx=-1;
		use=0;
		bool pd;
		memset(tong,0,sizeof(tong));
		for(re j=1;j<=n;j++)
		{
			while(j<=p&&j<=n)
			{
				tong[a[j]]++;
				mx=max(mx,a[j]);
				j++;
			}
			pd=0;
			cnt=j-1;
			while(1)
			{
				if(pd==0||cnt>=n)
				{
					change();
					A+=mx;
					tong[mx]--;
					change();
				}
				else if(cnt<n)
				{
					++cnt;
					if(a[cnt]>=mx)
					{
						A+=a[cnt];
					}
					else if(tong[mx])
					{
						A+=mx;
						tong[a[cnt]]++;
						tong[mx]--;
						change();
					}
					else
					{
						tong[a[cnt]]++;
						change();
						A+=mx;
						tong[mx]--;
						change();
					}
				}
				++use;
				if(use==n)
					break;
				if(cnt<n)
				{
					++cnt;
					if(a[cnt]>=mx)
					{
						B+=a[cnt];
					}
					else if(tong[mx])
					{
						B+=mx;
						tong[a[cnt]]++;
						tong[mx]--;
						change();
					}
					else
					{
						tong[a[cnt]]++;
						change();
						B+=mx;
						change();
					}
				}
				else
				{
					change();
					B+=mx;
					tong[mx]--;
					change();
				}
				++use;
				if(use==n)
					break;
				if(la==A&&lb==B)
					break;
				la=A;
				lb=B;
				pd=1;
			}
			break;
		}
		printf("%lld\n",A-B);
	}
	return 0;
}

T3 Park

一道DP好题,思路:设\(dp_{0,i,j}\)表示我从 i 的子树走到i,一共撒了 j 次的最大差值,设 \(dp_{1,i,j}\)表示从 i的father走向 i ,一共撒了 j 次的最大差值,那么状态转移方程可得 \(dp_{0,i,j}=max(dp_{0,i,j},max(dp_{0,son,j},dp_{0,son,j-1}+sum_i-p_{son}))\) ,\(dp_{1,i,j}=max(dp_{1,i,j},max(dp_{1,son,j},dp_{1,son,j-1}+sum_i-p_{father}))\)
但是,因为我们自上而下和自下而上计算可能会得到不一样的结果,所以我们还要反着做一遍DP
代码如下:

AC_Code


#include<bits/stdc++.h>
#define re register int
#define ii inline int
#define iv inline void
using namespace std;
const int N=1e5+10;
int n,v;
long long ans;
long long p[N],dp[2][N][110],sum[N];
vector<int> edge[N];
ii read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9')
	{
		if(ch=='-')
			f=0;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return (f)?x:(-x);
}
iv dfs(int x,int f)
{
	for(re i=1;i<=v;i++)
	{
		dp[0][x][i]=sum[x];
		dp[1][x][i]=sum[x]-p[f];
	}
	for(re i=0;i<edge[x].size();i++)
	{
		if(edge[x][i]==f)
			continue;
		dfs(edge[x][i],x);
		for(re j=1;j<v;j++)
			ans=max(ans,dp[0][x][j]+dp[1][edge[x][i]][v-j]);
		for(re j=1;j<=v;j++)
		{
			dp[0][x][j]=max(dp[0][x][j],max(dp[0][edge[x][i]][j],dp[0][edge[x][i]][j-1]+sum[x]-p[edge[x][i]]));
			dp[1][x][j]=max(dp[1][x][j],max(dp[1][edge[x][i]][j],dp[1][edge[x][i]][j-1]+sum[x]-p[f]));	
		}
	}
	
	reverse(edge[x].begin(),edge[x].end());
	for(re i=1;i<=v;i++)
	{
		dp[0][x][i]=sum[x];
		dp[1][x][i]=sum[x]-p[f];
	}
	for(re i=0;i<edge[x].size();i++)
	{
		if(edge[x][i]==f)
			continue;
		for(re j=1;j<v;j++)
			ans=max(ans,dp[0][x][j]+dp[1][edge[x][i]][v-j]);
		for(re j=1;j<=v;j++)
		{
			dp[0][x][j]=max(dp[0][x][j],max(dp[0][edge[x][i]][j],dp[0][edge[x][i]][j-1]+sum[x]-p[edge[x][i]]));
			dp[1][x][j]=max(dp[1][x][j],max(dp[1][edge[x][i]][j],dp[1][edge[x][i]][j-1]+sum[x]-p[f]));	
		}
	}
	for(re i=1;i<=v;i++)
		ans=max(ans,max(dp[0][x][i],dp[1][x][i]));
}
signed main()
{
	n=read();
	v=read();
	for(re i=1;i<=n;i++)
		scanf("%lld",&p[i]);
	int a,b;
	for(re i=1;i<n;i++)
	{
		a=read();
		b=read();
		edge[a].push_back(b);
		edge[b].push_back(a);
		sum[a]+=p[b];
		sum[b]+=p[a];
	}
	dfs(1,0);
	printf("%lld\n",ans);
	return 0;
}


原文地址:https://www.cnblogs.com/WindZR/p/15047307.html