namomo fish round1 A~C题解

本场链接:Namomo Fish(Easy) Round 1

闲话

D等tutorial来了再补上吧,感觉BC还是有点搞头的,不难但是比赛的时候没想明白.值得补写一点点内容.由于是中文题面就不给题面了.

A

显然第一个人也就两种走法,要么逆时针要么顺时针.先看逆时针,逆时针的时候就是两个人往中间跑会和,相当于在(n-m)的直线跑道上,有一个人从起点以((x+y))的速度跑完跑道,直接算时间.顺时针的时候有两种情况:一种是wls的速度是比较快的,也就是(y)的速度高一些,这个时候相当于追击问题,等价于一个人用(y-x)的速度跑(m)长度的跑道.另外一种情况是(y)比较小,这个时候相当于(x)在前面,离wls有(n-m)的距离,再进行追击,那么这个时候同样可以等价替换成(x-y)的人跑(n-m)长度的跑道.分别求最小值就可以了.

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

int main()
{
    int T;scanf("%d",&T);
    while(T--)
    {
		double n,m,x,y;scanf("%lf%lf%lf%lf",&n,&m,&x,&y);
    	double res = (n - m) / (x + y);
    	if(y > x)	res = min(res,m / (y - x));
    	else if(y < x)	res = min(res,(n - m) / (x - y));
    	printf("%lf
",res);
    }
    return 0;
}

B

任意一个上升序列是一个很离谱的条件,显然是要把这个条件转换成一个可以快速确定的条件的.不妨如此猜想:整个序列的(sum dist)应该只和开头和结尾的两个元素的位置相关,只有这样才可以做到说快速确定.

因为(sum dist)的奇偶性只和其中各个元素的奇偶性有关,不妨先把(p_1)出发的所有点二染色:曼哈顿距离是偶数的点和奇数的点分别进行划分.那么由于起点和终点确定,进一步可以推导出一个结论:如果(p_1)走到(p_n)的位置是偶数距离,那么中间不管怎么走,都不影响整个和是偶数的.因为经过的点无非两种:一种偶数距离的点,一种奇数距离的点,可以发现说对于每种类型的点之后走到终点,都一定会再增加一个自己同类型的距离,也就是说从(p_1)走到一个奇数点的时候,之后再不管怎么走走到终点的过程中一定会再有一个奇数距离的产生,因为不管怎么走都会从这个奇数距离的点走到一个偶数距离的点导致和还是一个偶数,偶数点同理没有影响.因此可以得到一个关键结论:只要(p_1)(p_n)的距离是偶数,那么任意的上升序列的(sum dist)就一定是一个偶数.

那么整个矩形有多少种构造方案呢,由于起点和终点非常特殊,组合数学的一个策略就是先安排特殊点.就按照定义把整个棋盘先二染色,那么棋盘上选择任意一个黑色的格子作为起点的时候,终点只能选黑色的,选择任意一个白色的格子作为起点的时候,终点只能选白色的.剩下的有((n*n-2)!)种选择,因为是任意的.那么只要求出整个局面上染色之后有多少一个白色点和黑色点,分别记作(cnto)(cntx),那么(res = (cnto * (cnto - 1) + cntx * (cntx - 1)) * (n * n - 2)!).

#define _CRT_SECURE_NO_WARNINGS
#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ll;
#define int ll
const int MOD = 1e9+7,N = 2e6 + 7;
int fact[N];

void init()
{
	fact[0] = 1;
	for(int i = 1;i < N;++i)
		fact[i] = (1ll*fact[i - 1] % MOD * i % MOD) % MOD;
}

signed main()
{
	ios::sync_with_stdio(0);cin.tie(0);
	init();
	int T;cin >> T;
	while(T--)
	{
		int n;cin >> n;
		if(n == 1)
		{
			cout << 1 << endl;
			continue;
		}
		int cntx,cnto;	
		if(n & 1)	cntx = (n * n) / 2,cnto = (n * n + 1) / 2;
		else		cntx = cnto = (n * n) / 2;
		int a = (1ll*cntx * (cntx - 1)) % MOD;
	    int b = (1ll*cnto * (cnto - 1)) % MOD;
	    int res = (a + b) % MOD;
	    res = (1ll*res * fact[n * n - 2] % MOD) % MOD;
	    cout << res << endl;
	}

    
    return 0;
}

C

这个题的条件很硬,没什么转换的策略.那么就从最单纯的暴力入手,可以发现每个(a_i)可以到达的值是非常有限的,个数非常少.所以不妨从每个元素(a_i)开始BFS.记录(dist[i][j])表示(a_i)变成(j)的步数以及(cnt[i])记录(i)这个数值在整个BFS过程中出现了几次,那么对于每个可能的取值来说,他应该出现了(n)次,才能使整个序列都变成他,在此前提下直接计算每一个元素到这个(i)的步数就可以了.由于能到的数字个数有限,这样暴力是的复杂度是正确的.不过由于(dist)里的数都很大,所以用map储存比较方便.

在具体的实现里,为了方便的标记没有出现的(dist),因此把初始的(dist[i][a[i]])的次数记成了(1).最后计算答案的时候要把他去掉.

#define _CRT_SECURE_NO_WARNINGS
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5+7;
int cnt[N],a[N];
map<int,int> dist[N];
int main()
{
	int n;scanf("%d",&n);
	for(int i = 1;i <= n;++i)	scanf("%d",&a[i]);

	for(int i = 1;i <= n;++i)
	{
		queue<int> q;cnt[a[i]] ++;dist[i][a[i]] = 1;q.push(a[i]);
		while(!q.empty())
		{
			int x = q.front();q.pop();
			
			ll y = 1ll * x * x;
			if(y <= 1e5 && !dist[i][y])
			{
				dist[i][y] = dist[i][x] + 1;
				++ cnt[y];
				q.push(y);
			}
			
			int y2 = sqrt(x);
			if(!dist[i][y2])
			{
				dist[i][y2] = dist[i][x] + 1;
				++ cnt[y2];
				if(y2 != 1)	q.push(y2);
			}
		}
	}
	
	ll res = 1e18;
	for(int tar = 1;tar <= 1e5;++tar)
	{
		if(cnt[tar] != n)	continue;
		ll cur = 0;
		for(int i = 1;i <= n;++i)
			cur += dist[i][tar] - 1;
		res = min(res,cur);
	}
	
	printf("%lld",res);
    return 0;
}
原文地址:https://www.cnblogs.com/HotPants/p/13588901.html