河南萌新联赛2024第(三)场:河南大学 C|K|A|D|M 题解

C-Circle_河南萌新联赛2024第(三)场:河南大学 (nowcoder.com)

对于该题,样例太有迷惑性了,但其实我们画图,画 n ( n ≥ 4 ) n(n\ge 4) n(n4) 个圆的时候,显然达不到 2 n 2^n 2n 的区域(或许画不出准确的)。

或许我们会想会不会是组合数 ∑ i = 1 n C n i = 2 n \sum_{i=1}^n C_n^i =2^n i=1nCni=2n 即一个圆,两个圆相交,三个相交,四个相交,这样的排列数,但是我们画图很显然,不可能达到这样的规模,我们可以举例:

想一下如果当前有四个圆,并且相交于一个区域,新增加进来的圆若想和周围四个圆相交而且仅为两两交,是不是需要在四个相交的地方添加一个圆,且该圆不会覆盖周围四个的圆,且分别与每个圆有交,其实会画出这样的图
请添加图片描述

很粗糙的图,也不想证明,但是很明显,这个半径是最小的,都无法出现一个新的区域是 五个圆相交的。 即上面的方法在这种粗略的证明下是错误的。

我们可以换一种思路,想每一次最多会添加多少个区域,或者说什么情况下才会添加区域?

其实会发现其实每次添加区域是在旧圆 上与 新圆相交的两个相邻的点,才会出现新的区域,那么我们可以考虑最多可以有几个交点?

假设上一次有 n − 1 n-1 n1 个圆,那么肯定只有 2 × ( n − 1 ) 2\times(n-1) 2×(n1) 个交点,因为任意两个圆直接只有 交,切,离,包含 四种,其中交的交点数最多为 2 2 2.

既然知道交点数,那可不可能两两之间就形成新的区域呢?

很显然这交点必然在新圆上,那么他们在新圆上必然都紧密相邻,所以最多增加 2 × ( n − 1 ) 2\times(n-1) 2×(n1) 个区域。那我们就能列方程了:
f 1 = 2 f n = f n − 1 + 2 ( n − 1 ) f_1 = 2\\ f_n = f_{n-1} + 2(n-1) f1=2fn=fn1+2(n1)
然后求通项,通项怎么求?
f 2 − f 1 = 2 ( 2 − 1 ) f 3 − f 2 = 2 ( 3 − 1 ) . . . f n − f n − 1 = 2 ( n − 1 ) f_2 - f_1= 2(2-1)\\ f_3 - f_2 = 2(3-1)\\ ...\\ f_n - f_{n-1} = 2(n-1) f2f1=2(21)f3f2=2(31)...fnfn1=2(n1)
然后每一项相加后移项就可以得到
f n = n 2 − n + 2 f_n = n^2 -n +2 fn=n2n+2

K-暴食之史莱姆_河南萌新联赛2024第(三)场:河南大学 (nowcoder.com)

这题我刚刚开始一直看错题,他可以吃掉 ≤ \le 他体积的。我一直看成 < < < ,想半天了,大家可以想一下如果 < < < 怎么写,我反正没想到。

既然是 ≤ \le 那么其实就对于当前 i i i 点只要维护从 1 → i 1\rightarrow i 1i 递增 和 i → n i\rightarrow n in 递减的最大序列即可。这两个之间显然互不影响,我们就可以去分别维护。

对于维护 1 → i 1 \rightarrow i 1i ,因为要递增,每一个数仅和前面一个数的取值有关系,所以我们很容易想到维护一个栈,然后看栈顶(即前一个元素) 是否大于自身,大于说明我们吃不掉,就把他丢掉,看栈下面一个。然后如果可以就转移,即在前一个 j j j 的基础上 + 1 +1 +1

维护 i → n i\rightarrow n in 的递减显然可以用上述方法来实现。 然后两者相加即可。

时间复杂度: O ( n ) O(n) O(n)

#include<bits/stdc++.h>
using namespace std;
#define ll long long  

signed main (){
	std::ios::sync_with_stdio(false);  
	cin.tie(NULL); 
	cout.tie(NULL);
	int n;
	cin>>n;
	vector<int> a(n+1);
	stack<int> s;
    vector<int> pre(n+1),pos(n+1);
    for(int i=1;i<=n;i++)
    	cin>>a[i];
    	
    for(int i=1;i<=n;i++)
    {
        while(!s.empty() && a[s.top()] > a[i] )
        	s.pop();
        if(!s.empty())
        {
        	pre[i] = pre[s.top()] +1;
        	//cout<<i<<' '<<s.top()<<endl;
        	
        }else 
        	pre[i] = 0;
        	
        s.push(i);
    }
    while(!s.empty())
    	s.pop();
    for(int i=n;i>=1;i--)
    {
    	while(!s.empty() && a[s.top()] > a[i] )
        	s.pop();
        
        if(!s.empty())
        	pos[i] = pos[s.top()] +1;
        else 
        	pos[i] = 0;
        	
        s.push(i);
    }
    for(int i=1;i<=n;i++)
    {	
    	cout<<pre[i]+pos[i]<<' ';
    }
    cout<<endl;
} 

A-圆周率日挑战_河南萌新联赛2024第(三)场:河南大学 (nowcoder.com)

显然是高精度题目 or python 题目,因为正常的值域在 1 0 6 10^6 106 内会有很小的误差,但是大于他或者小数位过多,都会造成 精度误差过大。

然后高精度大多数都是在整数域内进行,所以我们需要把圆周率整数一下,即 × 1 0 100 \times 10^{100} ×10100 即可。

然后对于除法我们肯定更乐意于加法,所以我们可以把 π = P 2 4 A → π × 4 A = P 2 \pi = \frac{P^2}{4A} \rightarrow \pi \times 4A = P^2 π=4AP2π×4A=P2 然后算差值最小即可。

复杂度为 O ( n × ( 100 × 1700 + 170000 ) ) O( n\times(100\times 1700 + 170000 )) O(n×(100×1700+170000)) 前面为乘法的复杂度,后面为减数的复杂度。

显然不行,也懒得优化了,就丢一点精度吧。直接 P 2 4 A \frac {P^2}{4A} 4AP2 拿去做减法算了。

其实维护 100 100 100 位的精度也是没有必要的,不妨想一下除法最大的小数位为多少?

1 1 0 17 \frac{1}{10^{17}} 10171 所以我们仅需维护多一倍保险 40 位即可。

#include<bits/stdc++.h>
using namespace std;
#define int long long
string pi = "31415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679";
//vector<pair<string,char>
// a-b  都是从小到大 最低位在 0 sub 返回是 abs值
// 初始 a > b 末尾都补 $10^2$ 个 0
string sub (string a,string b)
{	string ans ;
	if(a.size() < b.size())
		swap(a,b);
	else if(a.size() == b.size())
	{	
		int len = a.size();
		for(int i=len-1;i>=0;i--)
		{
			if(a[i] > b[i])
			{
				break;
			}	
			else if(a[i]==b[i])
				;
			else 
			{
				swap(a,b);
				break;
			}
		}
	}
	int lenb = b.size();
	for(int i=0;i<lenb;i++)
	{	int A = a[i] - '0';
		int B = b[i] - '0';
		if(A - B <0)
		{	
			a[i+1] -=1;
			// 要注意不能直接 + 因为可能爆char 直接被这个东西卡过
			// -1 不会
			ans.push_back((char)(A-B +10 + '0'));
		}
		else 
			ans.push_back((char)(A-B + '0'));
	}
	int lena = a.size();
	// 最后一位要判断一下是否还存在
	for(int i=lenb;i<lena;i++)
	{
		if(a[i] <'0')
		{
			a[i+1] -=1;
			ans.push_back((char)(a[i]+10));
		}
		else 
			ans.push_back((char)(a[i]));
	}
	while(--lena)
	{
		if(ans[lena] == '0')
			ans.pop_back();
		else 
			break;
	}
		
	return ans;	
}
int cmp(string a,string b) // a>b:1 a<b:0 a==b :2
{
	if(a.size() < b.size())
		return 0;
	else if(a.size() == b.size())
	{	
		int len = a.size();
		
		for(int i=len-1;i>=0;i--)
		{
			if(a[i] > b[i])
				return 1;
			else if(a[i]==b[i])
				;
			else 
				return 0;
		}
		return 2;	
	}
	return 1;
	
}

signed main (){
	std::ios::sync_with_stdio(false);  
	cin.tie(NULL); 
	cout.tie(NULL);
	
	for(int i=0;i<60;i++)
		pi.pop_back();
	// reverse 使得数位低的在数组低的
	reverse(pi.begin(),pi.end());
	
	
	// 处理数据 要 ×10^{40} 或者先除法,然后余数慢慢加入
	int	n;
	cin>>n;
	string mi = "00000000000000000000000000000000000000000000000000000000000000000001";
	int ansa =0,ansb=0;
	
	for(int i=1;i<=n;i++)
	{
		int p,q;
		cin>>p>>q;
		string a; //
		
		a = to_string (p/q) ;
		int res = p%q;
		for(int j=0;j<40;j++)
		{
			res = res*10;
			a.push_back((char)(res/q + '0'));
			res %=q;
		}
		//cout<<a.size()<<endl;
		
		
		reverse(a.begin(),a.end());
		string c = sub(a,pi);
		//cout<<c.size()<<endl;
		int now = cmp(mi,c);
		// 要记得判断等于情况
		if(now == 2 && ansa > p)
		{	
			ansa = p;
			ansb = q;
			mi = c;
		}
		else if(now==1)
		{
			ansa = p;
			ansb = q;
			mi = c;
		}
	}
	cout<<ansa<<' '<<ansb<<endl;
	
} 

D-开心消消乐(Right Version)_河南萌新联赛2024第(三)场:河南大学 (nowcoder.com)

由题意可以知道是从右往左的,即左边的操作不会影响右边的,那么左边肯定要消去后,才能往右边走,那么我们左边既然要消去,肯定要异或 自身,这个自身,可能前面一个数也是自身,那样就不会增加操作数,否则增加,但是 0 0 0 的时候要特判一下就好

#include<bits/stdc++.h>
using namespace std;
#define ll long long  
signed main (){
	std::ios::sync_with_stdio(false);  
	cin.tie(NULL); 
	cout.tie(NULL);
	int n;
	cin>>n;
	vector<int> a(n+1);
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
	}
	int now = 0;
	int ans = 0;
	for(int i=1;i<=n;i++)
	{
		if(a[i] == now)
		{
			;
		}
		else 
		{   if(a[i] == 0)
                ;
            else 
                ans ++;
			now = a[i];
         
		}
	}
	cout<<ans<<endl;
	
} 

M-推箱子_河南萌新联赛2024第(三)场:河南大学 (nowcoder.com)

暴力搜索,然后记录状态就号了,因为每个点就会被搜索一次所以复杂度 O ( 3 0 4 ) O(30^4) O(304)

#include<bits/stdc++.h>
using namespace std;
#define int long long  
const int N = 33;

int grid[N][N];
int dis[N][N][N][N];
pair<int,int> dir[] = {
	{1,0},{-1,0},{0,1},{0,-1}
};
int vis[N][N][N][N];

signed main (){
	std::ios::sync_with_stdio(false);  
	cin.tie(NULL); 
	cout.tie(NULL);
	int x1,x2,x3,y1,y2,y3;
	cin>>x1>>y1>>x2>>y2>>x3>>y3;
	int m;
	cin>>m;
	while(m--)
	{
		int x,y;
		cin>>x>>y;
		grid[x][y] = 1;
	}
	queue<array<int,4> > q;

	q.push({x1,y1,x3,y3});
	auto jud=[&] (int x,int y)
	{
		if(x<1 || y<1 || x>30 || y>30 || grid[x][y])
			return true;
		else 
			return false ;
	};
	auto ner=[&] (int px,int py,int bx,int by)
	{
		if(px == bx && by == py)
			return true;
		else
			return false ;
	};
	memset(dis,0x3f,sizeof dis);
	dis[x1][y1][x3][y3] = 0;
	
	while(!q.empty())
	{
		auto [px,py,bx,by] = q.front();
		q.pop();
		
		// if(px == 5 && py ==3 )
			// cout<<"case1"<<' '<<bx<<' '<<by<<endl;
			
		//cout<<px<<' '<<py<<' '<<bx<<' '<<by<<endl;
		
		if(bx == x2 && by == y2)
		{
			cout<<dis[px][py][bx][by]<<endl;
			return 0;
		}
		if(vis[px][py][bx][by])
			continue ;
		vis[px][py][bx][by] = 1;
		for(auto [dx,dy] : dir)
		{	
			
			if(jud(px+dx,py+dy))
			{
				continue ;
			}	
			// if(px == 6 && py == 5)
			// {	cout<<"case"<<endl;
				// cout<<dx<<' '<<dy<<endl;
				// cout<<bx<<' '<<by<<endl;
// 				
				// cout<<ner(px+dx,py+dy,bx,by)<<endl;
// 				
			// }	
			// if(px == 4 && py == 3 )
				// cout<<"case2:"<<px+dx<<' '<<py+dy<<' '<<bx<<' '<<by<<' '<<ner(px+dx,py+dy,bx,by)<<endl;
				
			if(ner(px+dx,py+dy,bx,by))
			{	
				
				if(jud(bx+dx,by+dy))
				{
					continue ;
				}
				else if(dis[px+dx][py+dy][bx+dx][by+dy] > dis[px][py][bx][by] + 1)
				{	
						
					dis[px+dx][py+dy][bx+dx][by+dy] = dis[px][py][bx][by] + 1;
					q.push({px+dx,py+dy,bx+dx,by+dy});
				}
					
			}
			else if(dis[px+dx][py+dy][bx][by] > dis[px][py][bx][by] + 1)
			{	
				// if(px == 4 && py == 3 )
					// cout<<"case1"<<' '<<bx<<' '<<by<<' '<<dx<<' '<<dy<<endl;
					
				//cout<<px+dx<<' '<<py+dy<<' '<<bx<<' '<<by<<endl;
				dis[px+dx][py+dy][bx][by] = dis[px][py][bx][by] + 1;
				q.push({px+dx,py+dy,bx,by});
				
			}
		}
	}
	cout<<-1<<endl;
	
	
	
} 

  • 19
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值