2021牛客暑期多校(一)

A: Alice and Bob

题目大意

两人博弈,每次一个人从一堆中拿 k k k 个,同时从另一堆拿 k ∗ s ( s > = 0 ) k * s(s >= 0) ks(s>=0) 个,问谁先不能拿。 10000 10000 10000 组数据, N < = 5000 N <= 5000 N<=5000

思路

暴力 S G SG SG 博弈问题
结论:如果某堆石子数量是 i i i,另一堆石子最多只有一种数量满足后手胜。
直接记录所有后手胜的 p a i r pair pair,每次对于一个 i i i,根据之前的 p a i r pair pair 去推导是否有一个满足后手胜的搭配 j j j。复杂度是 O ( N 2 l o g N ) O(N^2 log N) O(N2logN),实际速度近似 O ( N ) O(N) O(N)。(不会)
或类似筛法,对于一个后手状态 ( i , j ) (i,j) (i,j),它所转移出来的所有状态均为先手状态,起始 ( 0 , 0 ) (0,0) (0,0)为后手状态。且先手状态必被比它小的后手状态转移到。(只要能转移到后手状态即为先手状态,否则为后手状态。)

代码
#include <bits/stdc++.h>
using namespace std;
int t, n, m;
//先手-1, 后手-0
bool pr[5010][5010];
int main()
{
	for(int i = 0; i <= 5000; i++)
		for(int j = 0; j <= 5000; j++)
		{
			if(pr[i][j] == 0)
			{
				for(int k = 1; i+k <= 5000; k++)
					for(int l = 0; j+k*l <= 5000; l++)
						pr[i+k][j+k*l] = 1;
				for(int k = 1; j+k <= 5000; k++)
					for(int l = 0; i+k*l <= 5000; l++)
						pr[i+k*l][j+k] = 1;
                break;//每个i只有一个j
			}
		}
	scanf("%d", &t);
	while(t--)
	{
		scanf("%d %d", &n, &m);
		if(pr[n][m] == 0)
			printf("Bob\n");
		else
			printf("Alice\n");
	}
	return 0;
}

B: Ball Dropping

题目大意

一个球卡在一个直角等腰梯形内部,求卡着的高度。
在这里插入图片描述

思路

简单平面几何 浮点数

代码
#include <bits/stdc++.h>
using namespace std;
long double a, b, r, h, x, t;
int main()
{
	cin >> r >> a >> b >> h;
	if(b > 2*r)
		cout << "Drop";
	else
	{
		cout << "Stuck" << endl;
		x = b*h/(a-b);
		t = 2*r*sqrt(a*a/4 + (x+h)*(x+h))/a - x;
		cout << setprecision(10) << t;
	}
	return 0;
}

D: Determine the Photo Position

题目大意

给出一个 n ∗ n n*n nn 01 01 01 矩阵,要用一个 1 ∗ m 1*m 1m 的矩阵去覆盖一段 0 0 0,问方案数。

思路

签到,数每行连续 0 0 0 的个数。

代码
#include <bits/stdc++.h>
using namespace std;
int n, m, ans;
int main()
{
	scanf("%d %d", &n, &m);
	for(int i = 1; i <= n; i++)
	{
		int num = 0;
		string a;
		cin >> a;
		a += '1';
		for(int j = 0; j <= n; j++)
		{
			//cout << a[j] << endl;
			if(j == 0)
			{
				if(a[j] == '0')
					num = 1;
			}
			else
			{
				if(a[j] == '0' && a[j-1] == '0')
					num++;
				else if(a[j] == '0' && a[j-1] != '0')
					num = 1;
				else
				{
                	if(num >= m)
				    	ans += num-m+1;
					num = 0;
				}
			}
		}
	}		
	printf("%d\n", ans);
	return 0;
}

F: Find 3-friendly Integers

题目大意

定义一个自然数是 3 − f r i e n d l y 3-friendly 3friendly 的,如果它存在一个子串(允许前导 0 0 0)是 3 3 3 的倍数。多组数据,求 L − R L-R LR 3 − f r i e n d l y 3-friendly 3friendly 的数的个数。

思路

鸽笼原理
打表找规律?暴力 100 100 100 以内, 100 100 100 以外全部都是。

代码
#include <bits/stdc++.h>
using namespace std;
long long t, l, r;
int work(int z, int y)
{
	int ans = 0;
	for(int i = z; i <= y; i++)
	{
		int bj = 0;
		int a[10], x = i, cnt = 1;
		while(x > 0)
		{
			a[cnt++] = x%10;
			x /= 10;
		}
		cnt--;
		for(int j = 1; j <= cnt; j++)
		{
			int sum = 0;
			for(int k = j; k <= cnt; k++)
			{
				sum += a[k];
				if(sum % 3 == 0)
				{
					bj = 1;
					ans++;
					break;
				}
			}
			if(bj == 1)
				break;
		}
	}
	return ans;
}
int main()
{
	cin >> t;
	while(t--)
	{
		cin >> l >> r;
		if(l > 100)
			cout << r-l+1 << endl;
		if(r < 100)
			cout << work(l, r) << endl;
		else
			cout << r-100+work(l, 100) << endl;
	}
	return 0;
}

G: Game of Swapping Numbers

题目大意

给定序列 A , B A,B A,B,需要交换恰好 k k k A A A 中两个不同的数,使得 A , B A,B A,B 每个位置的绝对差值和最大。 N < = 100000 N <= 100000 N<=100000

思路

贪心 结论
由于是绝对值的差值,所以相当于根据相对大小给 A , B A,B A,B序列正负号,每次交换 A A A中的数对答案的影响来自对对应正负号的影响。所以 A , B A,B A,B正负号可以任意分配,不考虑步数的最优情况为大的 n n n个数分配正号,小的 n n n个数分配负号。由于步数限制,应该尽可能先交换对答案影响最大的一对数 A i A_i Ai A j A_j Aj。我们希望一次交换使答案更优,则应该选取的两个区间 [ A i , B i ] [A_i,B_i] [Ai,Bi] [ A j , B j ] [A_j,B_j] [Aj,Bj]是没有相交的,即在数轴上没有重叠,不妨设数轴上 [ A j , B j ] [A_j,B_j] [Aj,Bj] [ A i , B i ] [A_i,B_i] [Ai,Bi]的右面,则交换带来的影响为 2 ∗ [ m i n ( A j , B j ) − m a x ( A i , B i ) ] 2*[min(A_j,B_j)-max(A_i,B_i)] 2[min(Aj,Bj)max(Ai,Bi)]。为使结果更优,将 m i n ( A i , B i ) min(A_i,B_i) min(Ai,Bi) m a x ( A j , B j ) max(A_j,B_j) max(Aj,Bj)排序,选取差值前 k k k 大的数对相减取和。
结论: n > 2 n>2 n>2 时,恰好 k k k 步与至多 k k k 步是等价的。当 n > 2 n>2 n>2 时, A A A 中一定至少存在两个 + + + 号或两个 − - 号,此时如果我们交换这两个符号对应的数,则并不会使得原问题的解变得更劣。 n = 2 n=2 n=2 时需要特殊判断,因为强制交换 k k k 次可能使结果更差。

代码
#include <bits/stdc++.h>
using namespace std;
int n, k, a[500010], b[500010], minn[500010], maxx[500010];
long long sum, eff;
bool cmp(int a, int b)
{
	return a > b;
}
int main()
{
	scanf("%d %d", &n, &k);
	for(int i = 1; i <= n; i++)
		scanf("%d", &a[i]);
	for(int i = 1; i <= n; i++)
		scanf("%d", &b[i]);
	for(int i = 1; i <= n; i++)
	{
		sum += abs(a[i] - b[i]);
		if(a[i] > b[i])
		{
			minn[i] = b[i];
			maxx[i] = a[i];
		}
		else
		{
			minn[i] = a[i];
			maxx[i] = b[i];
		}
	}
	sort(minn+1, minn+n+1, cmp);
	sort(maxx+1, maxx+n+1);
	if(n == 2)
	{
		if(k%2 == 0)
			cout << abs(a[1]-b[1]) + abs(a[2]-b[2]) << endl;
		else
			cout << abs(a[1]-b[2]) + abs(a[2]-b[1]) << endl;
	}
	else
	{
		int cnt = 1;
		while(cnt <= n && cnt <= k)
		{
			if(minn[cnt] <= maxx[cnt])
				break;
			eff += 2*(minn[cnt] - maxx[cnt]);
			cnt++;
		}
		cout << sum + eff << endl;
	}
	return 0;
}

H: Hash Function

题目大意

题目大意:给定 n n n 个互不相同的数,找一个最小的模域,使得它们在这个模域下互不相同。 n = 500000 n=500000 n=500000

思路

假了。。。
答案一定是从 n n n m a x ( a i ) max(a_i) max(ai),对a数列排序后从大到小取模看是否符合条件。答案遍历越多,每次取模操作会越少,所以直接莽。数据过于水,没判断取模后会不会都取到一个位置也过了。。。
正解貌似是 F F T FFT FFT。。。
数据加强了,代码已更新。

代码
#include <bits/stdc++.h>
using namespace std;
int n, a[500010], wz[500010];
int main()
{
	scanf("%d", &n);
	for(int i = 1; i <= n; i++)
	{
		scanf("%d", &a[i]);
		wz[a[i]] = -1;
	}
	sort(a+1, a+n+1);
	for(int i = n; i <= a[n]+1; i++)
	{
		int bj = 1;
		for(int j = n; a[j] >= i; j--)
		{
            int cnt = a[j] - i;
            while(cnt >= 0)
            {
		        if(wz[cnt] == -1 || wz[cnt] == i)
		        {
			        bj = 0;
			        break;
		        }
                else
                    wz[cnt] = i;
                cnt -= i;
            }
            if(bj == 0)
                break;
	    }
	    if(bj == 1)
		{
			cout << i;
		    break;
		}
	}
	return 0;
}

K: Knowledge Test about Match

题目大意

随机生成一个权值范围为 0 − ( n − 1 ) 0-(n-1) 0(n1) 的序列,你要用 0 − ( n − 1 ) 0-(n-1) 0(n1) 去和它匹配,匹配函数是 s q r t sqrt sqrt 。要求平均情况下和标准值偏差不能超过 4 4 4% 。

思路

根号的凹凸性和常见函数相反,所以这题很难不通过匹配去求最优解。
主要要强调的是 s o r t sort sort ,直接 s o r t sort sort 其实是一个很糟糕的举动,因为 s q r t sqrt sqrt 的导函数随着 x x x 的增大值越来越小,而 s o r t sort sort 后很可能是层次不齐的情况,其实反而增大了函数和。举一个例子:{ 1 , 2 , 3 1,2,3 1,2,3}, { 0 , 1 , 2 0,1,2 0,1,2}, ( 1 , 1 ) , ( 2 , 2 ) , ( 0 , 3 ) (1,1), (2,2), (0, 3) (1,1),(2,2),(0,3) 会比 s o r t sort sort 的结果好很多。
从小到大枚举 d d d,每次贪心去看是否存在两数差 = d =d =d的数对,如果存在就暴力匹配上去。

代码
#include <bits/stdc++.h>
using namespace std;
bool cmp(int a, int b)
{
	return a > b;
}
int main()
{
	int t;
	scanf("%d", &t);
	while(t--)
	{
		int n;
		scanf("%d", &n);
		int a[1010], b[1010], c[1010];
		for(int i = 0; i < n; i++)
		{
			a[i] = -1;
			scanf("%d", &b[i]);
		}
		int cnt = 0;
		for(int k = 0; ; k++)
		{
			for(int i = 0; i < n; i++)
			{
				if(b[i] == -1)
					continue;
				for(int j = 0; j < n; j++)
				{
					if(a[j] != -1)
						continue;
					if(abs(b[i]-j) == k)
					{
						a[j] = b[i];
						b[i] = -1;
						cnt++;
						break;
					}
				}
			}
			if(cnt == n)
				break;
		}
		for(int i = 0; i < n; i++)
			printf("%d ", a[i]);
		printf("\n");
	}
	return 0;
}

还是没补期望 D P DP DP,呜呜。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值