zoukankan      html  css  js  c++  java
  • 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;
    }
    
    
    
  • 相关阅读:
    C语言学习代码〈二〉
    C语言学习笔记<三 >
    C语言学习笔记<一 >
    C语言学习代码〈四〉
    C语言学习代码〈三〉
    C语言学习笔记<五>
    C语言学习笔记<四>
    C语言学习笔记<二 >
    C语言学习代码<一> .
    周末练习
  • 原文地址:https://www.cnblogs.com/WindZR/p/15047307.html
Copyright © 2011-2022 走看看