2024初三集训模拟测试1

2024初三集训模拟测试1

T1 edit 100pts

  • 字符串模拟即可。

  • 貌似不能写成 while(cin>>s) ,因为每两个单词中可能不只有一个空格。

    点击查看代码
    string s;
    int main()
    {
    	freopen("edit.in","r",stdin);
    	freopen("edit.out","w",stdout);
    	getline(cin,s);
    	cout<<s[0];
    	for(int i=1;i<s.size();i++)
    	{
    		if(('0'<=s[i-1]&&s[i-1]<='9'&&(('a'<=s[i]&&s[i]<='z')||('A'<=s[i]&&s[i]<='Z')))||((('a'<=s[i-1]&&s[i-1]<='z')||('A'<=s[i-1]&&s[i-1]<='Z'))&&'0'<=s[i]&&s[i]<='9'))
    		{
    			cout<<" ";
    		}
    		cout<<s[i];
    	}
    	fclose(stdin);
    	fclose(stdout);
    	return 0;
    }
    

T2 game 50pts

  • 感觉和 luogu P8818 [CSP-S 2022] 策略游戏 很像,但事实证明读假题了。
  • 部分分
    • 测试点 1
      • 10pts :由于 n=1 ,故 a1 即为所求。
    • 测试点 23
      • 20pts :由于 0ai106 ,故 i=1nai 即为所求。
    • 测试点 45
      • 20pts :由于 106ai<0 ,故 mini=1n{ai}+i=1nai 即为所求。
  • 正解
    • Alice 一定只会在第一轮取大于等于 1 个数,且 Alice 会取完所有的非负数。然后两人轮流取最大值。故对 a 进行排序。
    • 贪心
      • Alice 第一轮取负数进行分讨。

        • 赛时及发现 hack 数据前只有 @wang54321DP 做法幸免于难。

          点击查看 hack 数据
          input:
          5
          -5121313 -81 -713 -25 -479
          
          ans:
          -819
          
          
        点击查看代码
        ll a[200000],b[200000];
        int main()
        {
        	freopen("game.in","r",stdin);
        	freopen("game.out","w",stdout);
        	ll n,m=0,ans=0,i;
        	cin>>n;
        	for(i=1;i<=n;i++)
        	{
        		cin>>a[i];
        		if(a[i]<0)
        		{
        			m++;
        			b[m]=a[i];
        		}
        		ans+=(a[i]>=0)*a[i];
        	}
        	sort(b+1,b+1+m,greater<int>()); 
        	if(n==m)
        	{
        		if(m%2==1)//取1 2 4 6 ...
        		{
        			ans+=b[1];//特判
        			for(i=2;i<=m;i++)
        			{
        				ans+=(i%2==0)*b[i];
        			}
        		}
        		else//取1 3 5 7 ...
        		{
        			for(i=1;i<=m;i++)
        			{
        				ans+=(i%2==1)*b[i];
        			}
        		}
        	}
        	else
        	{
        		if(m%2==1)//取2 4 6 ...
        		{
        			for(i=1;i<=m;i++)
        			{
        				ans+=(i%2==0)*b[i];
        			}
        		}
        		else//取1 3 5 7 ...
        		{
        			for(i=1;i<=m;i++)
        			{
        				ans+=(i%2==1)*b[i];
        			}
        		}
        	}
        	cout<<ans<<endl;
        	fclose(stdin);
        	fclose(stdout);
        	return 0;
        }
        
    • DP
      • fi,0 表示第 i 个数 Bob 选的最大得分, fi,1 表示第 i 个数 Alice 选的最大得分。状态转移方程为 {fi,0=fi1,1fi,1=max(fi1,0,fi1,1)+ai
        • Alice 第一轮取负数进行分讨。具体地,规定 {f1,0=f1,1=a1
      • 最终,有 max(fn,0,fn,1) 即为所求。

T3 score 50pts

  • 简化题意:给定一个长度为 n 的序列 a ,求 l=1nr=ln[(k=lrak)mod(rl+1)=0]

  • 部分分

    • 测试点 15
      • 50pts :前缀和优化暴力枚举。时间复杂度为 O(n2)
    • 测试点 67
      • 20pts :由于 0ai2 ,故对于每段最长的一段连续区间 [l,r] 满足 al=al+1=al+2==ar ,则这段区间对答案产生的贡献为 (rl+12)=(rl+1)(rl)2
  • 正解

    • l=1nr=ln[(k=lrak)mod(rl+1)=0] 等价于 l=1nr=ln[k=lr(akk=lrakrl+1)=0]
    • 观察到 0ai100 ,考虑枚举 k=lrakrl+1=x(mini=1n{ai}xmaxi=1n{ai})
    • 使用前缀和优化 k=lr(akx) 。具体地,有 sumi=k=1i(akx) 。此时有 l=1nr=ln[k=lr(akx)=0]=l=1nr=ln[sumrsuml1=0]=l=1nr=ln[sumr=suml1]
    • 用桶来记录 sum 出现的次数即可。
      • 因存在负数的情况,故将得到的 sum 加上一个比较大的数。
      • 记得要处理 sum0
    点击查看代码
    ll a[200001],sum[200001],vis[21000001];
    int main()
    {
    	freopen("score.in","r",stdin);
    	freopen("score.out","w",stdout);
    	ll n,ans=0,minn=0x7f7f7f7f,maxx=0,i,j;
    	cin>>n;
    	for(i=1;i<=n;i++)
    	{
    		cin>>a[i];
    		minn=min(minn,a[i]);
    		maxx=max(maxx,a[i]);
    	}
    	for(i=minn;i<=maxx;i++)
    	{
    		vis[sum[0]+maxx*n]=1;
    		for(j=1;j<=n;j++)
    		{
    			sum[j]=sum[j-1]+a[j]-i;
    			ans+=vis[sum[j]+maxx*n];
    			vis[sum[j]+maxx*n]++;
    		}
    		for(j=0;j<=n;j++)
    		{
    			vis[sum[j]+maxx*n]=0;
    		}
    	}
    	cout<<ans<<endl;
    	fclose(stdin);
    	fclose(stdout);
    	return 0;
    }
    

T4 city 75pts

  • 部分分

    • 60pts :输出 -1
    • 测试点 57
      • 15pts :由于 x=1 ,故只有一个强连通分量(环)。此时 m 需满足 nmAn2=(n1)n 时有解,构造时先拿出 n 条边使得 1n 构成一个环,剩下的随便构造即可(上界为完全图);否则无解。

        点击查看代码
        if(x==1)
        {
        	sum=0;
        	if(n<=m&&m<=n*(n-1))
        	{
        		for(i=1;i<=n-1;i++)
        		{
        			cout<<i<<" "<<i+1<<endl;
        		}
        		cout<<n<<" "<<1<<endl;
        		sum=n;
        		if(sum<m)
        		{
        			for(i=1;i<=n-1;i++)
        			{
        				for(j=1;j<=n;j++)
        				{
        					if(j!=i&&j!=i+1)
        					{
        						sum++;
        						cout<<i<<" "<<j<<endl;
        						if(sum==m)
        						{
        							break;
        						}
        					}
        				}
        				if(sum==m)
        				{
        					break;
        				}
        			}
        			if(sum<m)
        			{
        				i=n;
        				for(j=1;j<=n;j++)
        				{
        					if(j!=n&&j!=1)
        					{
        						sum++;
        						cout<<i<<" "<<j<<endl;
        						if(sum==m)
        						{
        							break;
        						}
        					}
        				}
        			}
        		}
        	}
        	else
        	{
        		cout<<-1<<endl;
        	}
        }
        
    • 测试点 810
      • 15pts :由于 x=n ,故有 n 个强连通分量(孤立点)。此时 q,m 需满足 q=0,0m(n2)=(n1)n2 时有解,构造时随便构造;否则无解。

        点击查看代码
        if(x==n)
        {
        	sum=0;
            if(q==0)
            {
                if(m<=(n-1+1)*(n-1)/2)
                {
                    for(i=1;i<=n-1;i++)
                    {
                        for(j=i+1;j<=n;j++)
                        {
                            sum++;
                            cout<<i<<" "<<j<<endl;
                            if(sum==m)
                            {
                                break;
                            }
                        }
                        if(sum==m)
                        {
                            break;
                        }
                    }
                }
                else
                {
                    cout<<-1<<endl;
                }
            }
            else
            {
                cout<<-1<<endl;
            }
        }
        
  • 正解

    • ai,bi 属于同一个强连通分量,但并没有其具体的边的方向,故可以使用并查集维护连通性,求出此时的强连通分量 scc 数量 num
      • 孤立的一个点也属于一个强连通分量。
    • numx ,则将 scc 进行任意合并至只有 x 个强连通分量;否则无解。
    • 求出边数的最小值 mmin 和最大值 mmax
      • 对于第 i 个强连通分量,若 |scci|=1 ,则其内部没有边,否则其内部最少有 |scci| 条边,最多有 Ascci2=(scci1)scci 条边。
        • 最少情况是一个环。
        • 最多情况是一个完全子图。
      • 对于第 i 和第 j 个强连通分量,需保证 ij 不能形成一个更大的强连通分量,故 ij 之间最少有 0 条边,最多有 (|scci|1)(|sccj|1)=|scci|×|sccj| 条边。
        • 最多情况是对于 scci 中的每个点,均向 sccj 中的每个点连一条有向边或对于 sccj 中的每个点,均向 scci 中的每个点连一条有向边。
    • mminmmmax ,则有解;否则无解。
    • 构造时,先将属于同一个强连通分量内的点形成一个强连通分量,此时可用边数减少了 mmin ;接着构造剩下的 mmmin 条边,类比求边数的最小、最大值的构造方式即可。
    点击查看代码
    ll f[600000],c[600000],sum[600000],ans=0; 
    vector<ll>scc[600000];
    ll find(ll x)
    {
    	return (f[x]==x)?x:f[x]=find(f[x]);
    }
    void merge(ll x,ll y)
    {
    	x=find(x);
    	y=find(y);
    	if(x!=y)
    	{
    		f[x]=y;
    	}
    }
    bool cmp(vector<ll>a,vector<ll>b)
    {
    	return a.size()>b.size();
    }
    int main()
    {
    	freopen("city.in","r",stdin);
    	freopen("city.out","w",stdout);
    	ll n,m,mm=0,x,q,a,b,minn=0,maxx=0,i,j,k,h;
    	cin>>n>>m>>x>>q;
    	for(i=1;i<=n;i++)
    	{
    		f[i]=i;
    	}
    	for(i=1;i<=q;i++)
    	{
    		cin>>a>>b;
    		merge(a,b);
    	}
    	for(i=1;i<=n;i++)
    	{
    		if(f[i]==i)
    		{
    			ans++;
    			c[i]=ans;
    		}
    	}
    	for(i=1;i<=n;i++)
    	{
    		scc[c[find(i)]].push_back(i);
    	}
    	if(x<=ans)
    	{   
    		sort(scc+1,scc+1+ans,cmp);
    		for(i=x+1;i<=ans;i++)
    		{
    			for(j=0;j<scc[i].size();j++)
    			{
    				scc[x].push_back(scc[i][j]);
    			}
    		}
    		for(i=1;i<=x;i++)
    		{
    			minn+=(scc[i].size()>=2)*scc[i].size();
    			maxx+=(scc[i].size()>=2)*scc[i].size()*(scc[i].size()-1);
    			sum[i]=sum[i-1]+scc[i].size();
    		}
    		for(i=1;i<=x;i++)
    		{
    			maxx+=scc[i].size()*(sum[x]-sum[i]);
    		}
    		if(minn<=m&&m<=maxx)
    		{
    			for(i=1;i<=x;i++)
    			{
    				if(scc[i].size()>=2)
    				{
    					for(j=0;j+1<scc[i].size();j++)
    					{
    						cout<<scc[i][j]<<" "<<scc[i][j+1]<<endl;
    						mm++;
    					}
    					cout<<scc[i][scc[i].size()-1]<<" "<<scc[i][0]<<endl;
    					mm++;
    				}
    			}
    			if(mm<m)
    			{
    				for(i=1;i<=x;i++)
    				{
    					for(j=0;j<scc[i].size();j++)
    					{
    						for(h=0;h<scc[i].size();h++)
    						{
    							if(h!=j&&h!=j+1&&(!(j==scc[i].size()-1&&h==0)))
    							{
    								cout<<scc[i][j]<<" "<<scc[i][h]<<endl;
    								mm++;
    								if(mm==m)
    								{
    									break;
    								}
    							}
    						}
    						if(mm==m)
    						{
    							break;
    						}
    						for(k=i+1;k<=x;k++)
    						{
    							for(h=0;h<scc[k].size();h++)
    							{
    								cout<<scc[i][j]<<" "<<scc[k][h]<<endl;
    								mm++;
    								if(mm==m)
    								{
    									break;
    								}
    							}
    							if(mm==m)
    							{
    								break;
    							}
    						}
    						if(mm==m)
    						{
    							break;
    						}
    					}
    					if(mm==m)
    					{
    						break;
    					}
    				}
    			}
    		}
    		else
    		{
    			cout<<"-1"<<endl;
    		}
    	}   
    	else
    	{
    		cout<<"-1"<<endl;
    	}
    	fclose(stdin);
    	fclose(stdout);
    	return 0;
    }
    

总结

  • 赛时估分 100+30+50+30=210pts ,实际得分 100+50+50+75=275pts
    • 部分分造得很足。
  • T2 贪心想假了。
  • T3 测试点 6720pts 没写。

后记

  • 数据有点水,明显的 hack 数据没有加。
  • T4 赛时没有 Special Judge ,所以开的“文本比较”。赛后 @wkh2008@xrlong 的代码把 Special Judge 写了。但 Special Judge 很“脆弱”,放过了部分错解,但只要不太离谱就不会挂。
posted @   hzoi_Shadow  阅读(70)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
扩大
缩小
点击右上角即可分享
微信分享提示