广东工业大学2020级年ACM第一次月赛

比赛地址

A-骗红包

题意

[ 1 , 1000 ] [1,1000] [1,1000]中随机选择一个整数 n n n,zf和zn轮流操作,zf先手,每轮可以执行以下操作之一:

  1. n = ⌊ n 2 ⌋ n = \lfloor\frac{n}{2}\rfloor n=2n;
  2. n = n − 1 n = n - 1 n=n1;

先把 n n n变为 0 0 0的玩家赢,获得 n n n个硬币。先进行 1000 1000 1000次游戏,求zf获得硬币数的期望。

思路

先考虑 n n n是定值时的情况。设 g [ n ] g[n] g[n]表示选择的数字为 n n n,先手是否必胜, g [ n ] = 1 g[n]=1 g[n]=1表示先手必胜, g [ n ] = 0 g[n]=0 g[n]=0表示先手必败。显然当数字 ⌊ n 2 ⌋ \lfloor\frac{n}{2}\rfloor 2n或数字 n − 1 n - 1 n1先手必败时,数字 n n n先手必胜。所以有转移式 g [ n ] = ! g [ n − 1 ] ∣ ∣ ! g [ n / 2 ] g[n]=!g[n-1]||!g[n/2] g[n]=!g[n1]!g[n/2]
n n n [ 1 , 1000 ] [1,1000] [1,1000]中随机整数时,每次游戏每个数被选的概率为 1 1000 \frac{1}{1000} 10001,所以一次游戏的硬币数期望为 ∑ i = 1 1000 g [ i ] ∗ i ∗ 1 1000 \sum\limits_{i=1}^{1000}g[i]*i*\frac{1}{1000} i=11000g[i]i10001 1000 1000 1000次游戏的硬币数期望为 ∑ i = 1 1000 g [ i ] ∗ i \sum\limits_{i=1}^{1000}g[i]*i i=11000g[i]i

代码

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

#define fo(i, x, y) for (int i = (x); i <= (y); ++i)
#define fd(i, x, y) for (int i = (x); i >= (y); --i)

const int maxn = 1e3 + 5;

int g[maxn];

int getint()
{
	char ch;
	int res = 0, p;
	while (!isdigit(ch = getchar()) && (ch ^ '-'));
	p = ch == '-'? ch = getchar(), -1 : 1;
	while (isdigit(ch))
		res = (res << 3) + (res << 1) + (ch ^ 48), ch = getchar();
	return res * p;
}

int main()
{
	g[1] = 1;
	fo(i, 2, 1000) g[i] = !g[i - 1] || !g[i / 2];
	int ans = 0;
	fo(i, 1, 1000) ans += g[i] * i;
	printf("%d\n", ans); 
	return 0;
}

B-的面包工坊

题意

给定 n n n,需要寻找一个长度为 m m m(自己确定)的数列 { a i } \{a_i\} {ai},其中 n = ∑ i = 1 m a i n=\sum\limits_{i=1}^{m}a_i n=i=1mai,求 ∏ i = 1 m a i \prod\limits_{i=1}^{m}a_i i=1mai的最大值。

思路

f [ i ] f[i] f[i]为和为 i i i乘积的最大值,对于每个和 i i i,可以枚举最后一个数 j j j的大小,又由于和为 i − j i-j ij的乘积最大值已知,所以有转移方程 f [ i ] = max ⁡ { f [ i − j ] ∗ j } ( 1 ≤ j ≤ i ) f[i]=\max\{f[i-j]*j\}(1 \leq j \leq i) f[i]=max{f[ij]j}(1ji)

代码

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

#define fo(i, x, y) for (int i = (x); i <= (y); ++i)
#define fd(i, x, y) for (int i = (x); i >= (y); --i)

typedef long long ll;

const int maxn = 100 + 5;

int n;
ll f[maxn];

int getint()
{
	char ch;
	int res = 0, p;
	while (!isdigit(ch = getchar()) && (ch ^ '-'));
	p = ch == '-'? ch = getchar(), -1 : 1;
	while (isdigit(ch))
		res = (res << 3) + (res << 1) + (ch ^ 48), ch = getchar();
	return res * p;
}

int main()
{
	int T;
	T = getint();
	while (T--)
	{
		n = getint();
		f[0] = 1;
		fo(i, 1, n)
			fo(j, 1, i)
				f[i] = max(f[i], f[i - j] * j);
		printf("%lld\n", f[n]);
	}
	return 0;
}

C-tmk一衣带水

题意

思路

代码

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

#define fo(i, x, y) for (int i = (x); i <= (y); ++i)
#define fd(i, x, y) for (int i = (x); i >= (y); --i)

int getint()
{
	char ch;
	int res = 0, p;
	while (!isdigit(ch = getchar()) && (ch ^ '-'));
	p = ch == '-'? ch = getchar(), -1 : 1;
	while (isdigit(ch))
		res = (res << 3) + (res << 1) + (ch ^ 48), ch = getchar();
	return res * p;
}

int main()
{
	printf("tmknb!");
	return 0;
}

D-三角切

题意

给定三条边的长度,问是否能组成三角形,若能组成三角形,则输出三角形的类型(锐角,直角,钝角)。

思路

考虑能组成三角形的情况。假定三边分别为 a , b , c a,b,c a,b,c a < b < c a<b<c a<b<c。分情况考虑:

  1. 直角: c 2 = a 2 + b 2 c^2=a^2+b^2 c2=a2+b2;
  2. 钝角,考虑一种极限情况,当 a + b a+b a+b无限接近 c c c时,有 c = a + b c=a+b c=a+b,此时 c 2 > a 2 + b 2 c^2>a^2+b^2 c2>a2+b2
  3. 锐角,结合1.2.可得 c 2 < a 2 + b 2 c^2<a^2+b^2 c2<a2+b2

具体证明可以建系用向量点乘,自行思考。

代码

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

#define fo(i, x, y) for (int i = (x); i <= (y); ++i)
#define fd(i, x, y) for (int i = (x); i >= (y); --i)

typedef long long ll;

int getint()
{
	char ch;
	int res = 0, p;
	while (!isdigit(ch = getchar()) && (ch ^ '-'));
	p = ch == '-'? ch = getchar(), -1 : 1;
	while (isdigit(ch))
		res = (res << 3) + (res << 1) + (ch ^ 48), ch = getchar();
	return res * p;
}

int main()
{
	int T;
	T = getint();
	while (T--)
	{
		ll l[5];
		l[1] = getint(); l[2] = getint(); l[3] = getint();
		sort(l + 1, l + 1 + 3);
		if (l[1] + l[2] <= l[3]) printf("No Solution\n");
		else if (l[1] * l[1] + l[2] * l[2] == l[3] * l[3]) printf("Right\n");
		else if (l[1] * l[1] + l[2] * l[2] < l[3] * l[3]) printf("Obtuse\n");
		else printf("Acute\n");
	}
	return 0;
}

E-素数判定

题意

如题。

思路

自行查书,百度。

代码

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

#define fo(i, x, y) for (int i = (x); i <= (y); ++i)
#define fd(i, x, y) for (int i = (x); i >= (y); --i)

int getint()
{
	char ch;
	int res = 0, p;
	while (!isdigit(ch = getchar()) && (ch ^ '-'));
	p = ch == '-'? ch = getchar(), -1 : 1;
	while (isdigit(ch))
		res = (res << 3) + (res << 1) + (ch ^ 48), ch = getchar();
	return res * p;
}

bool prime(int x)
{
	if (x == 1) return false;
	if (x == 2) return true;
	for(int i = 2; i * i <= x; i++)
		if (x % i == 0) return false;
	return true;
}

int main()
{
	int T;
	T = getint();
	while (T--)
	{
		printf(prime(getint())? "Yes\n" : "No\n");
	}
	return 0;
}

F-K阶Mex数列

题意

定义 m e x ( l , r ) mex(l,r) mex(l,r) { a i ∣ l ≤ i ≤ r } \{a_i|l \leq i \leq r\} {ailir}中最小不存在的非负整数,其中 { a i } \{a_i\} {ai}为待求数列。给定 k , n k,n k,n,结合以下式子求 a n a_n an
a n = { n n < k m e x ( n − k , n − 1 ) n ≥ k a_n=\begin{cases} n & n<k \\ mex(n-k,n-1) & n \geq k \end{cases} an={nmex(nk,n1)n<knk

思路

经模拟发现, { a n } \{a_n\} {an} 0 , 1 , ⋯   , k − 1 , k , 0 , 1 , ⋯   , k − 1 , k 0,1,\cdots,k-1,k,0,1,\cdots,k-1,k 0,1,,k1,k,0,1,,k1,k不断循环,可得答案为 n % ( k + 1 ) n\%(k+1) n%(k+1)

代码

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

#define fo(i, x, y) for (int i = (x); i <= (y); ++i)
#define fd(i, x, y) for (int i = (x); i >= (y); --i)

int getint()
{
	char ch;
	int res = 0, p;
	while (!isdigit(ch = getchar()) && (ch ^ '-'));
	p = ch == '-'? ch = getchar(), -1 : 1;
	while (isdigit(ch))
		res = (res << 3) + (res << 1) + (ch ^ 48), ch = getchar();
	return res * p;
}

int main()
{
	int T = getint();
	while (T--)
	{
		int n, k;
		n = getint(); k = getint();
		printf("%d\n", n % (k + 1));
	}
	return 0;
}

G-秧歌Star不要上补习班

题意

给定二维坐标系中若干个点 ( x i , y i ) (x_i,y_i) (xi,yi),每个点有权值 v i v_i vi
接下来有次个询问,每次询问一个矩形/一条线段上的所有点的权值之和。

思路

解法一

用二维前缀和预处理权值之和,每次 O ( 1 ) O(1) O(1)查询,不懂二维前缀和自行百度。

解法二

对于每个询问,枚举所有点判断是否在给定矩形/线段上,若是则累加权值。

代码

解法一

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

#define fo(i, x, y) for (int i = (x); i <= (y); ++i)
#define fd(i, x, y) for (int i = (x); i >= (y); --i)

const int maxn = 1e3 + 5;

int n, m, q, k;
int s[maxn][maxn];

int getint()
{
	char ch;
	int res = 0, p;
	while (!isdigit(ch = getchar()) && (ch ^ '-'));
	p = ch == '-'? ch = getchar(), -1 : 1;
	while (isdigit(ch))
		res = (res << 3) + (res << 1) + (ch ^ 48), ch = getchar();
	return res * p;
}

int main()
{
	int T;
	T = 1;
	while (T--)
	{
		n = getint(); m = getint(); k = getint();
		fo(i, 1, n)
			fo(j, 1, m) s[i][j] = 0;
		fo(i, 1, k)
		{
			int x, y, v;
			x = getint(); y = getint(); v = getint();
			s[x][y] = v;
		}
		fo(i, 1, n)
			fo(j, 1, m)
				s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + s[i][j];
		q = getint();
		fo(i, 1, q)
		{
			int x, y, xx, yy, u, d, l, r;
			x = getint(); xx = getint(); y = getint(); yy = getint();
			u = min(x, xx); d = max(x, xx);
			l = min(y, yy); r = max(y, yy);
			printf("%d\n", s[d][r] - s[d][l - 1] - s[u - 1][r] + s[u - 1][l - 1]);
		}
	}
	return 0;
}

解法二

自己写

H 一道难题

题意

给定 n , m n,m n,m,表示以下数列:
1 , 2 , . . . , m − 1 , m , 1 , 2 , ⋯ 1,2,...,m-1 ,m,1,2,\cdots 1,2,...,m1,m,1,2, 1 1 1 m m m循环)总共 n n n项。可以删除数列中的若干个数(包括不删和全删),求剩下的项组成的数列的种类数。当两个数列长度不同或有某位不同,则这两个数列为不同种类的数列。

思路

为了简洁起见下面称“剩下的项组成的数列的种类数”为“方案数”,“一个数列执行若干或不执行删除操作后得到的数列”为“剩余数列”。
考虑 d p dp dp,设 f [ i ] [ j ] f[i][j] f[i][j]为前 i i i个数,以 j j j结尾的剩余数列方案数。设数列第 i i i个数为 a i a_i ai,则:当 j ≠ a i j \neq a_i j=ai时,显然有 f [ i ] [ j ] = f [ i − 1 ] [ j ] f[i][j]=f[i-1][j] f[i][j]=f[i1][j],下面考虑 j = a i j=a_i j=ai的情况。
按照前 i − 1 i-1 i1个数剩余数列结尾的 a i a_i ai数量进行分类讨论:

  1. i − 1 i-1 i1个数的剩余数列为空,则方案数为 1 1 1
  2. i − 1 i-1 i1个数的剩余数列结尾的 a i a_i ai数量为0,则剩余数列以 a i a_i ai以外的数结尾。
  3. i − 1 i-1 i1个数的剩余数列结尾的 a i a_i ai数量 ≥ 1 \geq 1 1,则剩余数列以 a i a_i ai结尾。

综上,当 j ≠ a i j \neq a_i j=ai时, f [ i ] [ j ] = 1 + ∑ k = 1 m f [ i − 1 ] [ k ] f[i][j]=1+\sum\limits_{k=1}^m f[i-1][k] f[i][j]=1+k=1mf[i1][k],方便起见,可以设 f [ i ] [ 0 ] f[i][0] f[i][0]为空数列,令 f [ i ] [ 0 ] = 1 f[i][0]=1 f[i][0]=1,则:
f [ i ] [ j ] = { f [ i − 1 ] [ j ] j ≠ a i ∑ k = 0 m f [ i − 1 ] [ k ] j = a i f[i][j]=\begin{cases} f[i-1][j] & j \neq a_i \\ \sum\limits_{k=0}^m f[i-1][k]& j=a_i \end{cases} f[i][j]=f[i1][j]k=0mf[i1][k]j=aij=ai
根据 f [ i ] [ j ] f[i][j] f[i][j]定义可得最终答案为 ∑ i = 0 m f [ n ] [ i ] \sum\limits_{i=0}^m f[n][i] i=0mf[n][i]
进一步观察式子可以发现,对于每个 i i i只有 f [ i ] [ a i ] f[i][a_i] f[i][ai]发生改变,且 ∑ k = 0 m f [ i − 1 ] [ k ] \sum\limits_{k=0}^m f[i-1][k] k=0mf[i1][k]为更新前的 f [ i − 1 ] f[i-1] f[i1]数组的和,可以考虑用变量 s u m sum sum维护该和,每次更新 s u m sum sum即可,细节看代码。

代码

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

#define fo(i, x, y) for (int i = (x); i <= (y); ++i)
#define fd(i, x, y) for (int i = (x); i >= (y); --i)

typedef long long ll;

const int maxn = 1e6 + 5;
const ll mod = 1e9 + 7;

int n, m;
ll f[maxn];

int getint()
{
	char ch;
	int res = 0, p;
	while (!isdigit(ch = getchar()) && (ch ^ '-'));
	p = ch == '-'? ch = getchar(), -1 : 1;
	while (isdigit(ch))
		res = (res << 3) + (res << 1) + (ch ^ 48), ch = getchar();
	return res * p;
}

int main()
{
	int T = getint();
	while (T--)
	{
		n = getint(); m = getint();
		f[0] = f[1] = 1;
		ll sum = 2;
		fo(i, 2, m) f[i] = 0;
		fo(i, 2, n)
		{
			int now = (i - 1) % m + 1;
			ll nsum = (sum * 2 % mod - f[now] + mod) % mod;
			f[now] = sum;
			sum = nsum;
		}
		ll ans = 0;
		fo(i, 0, m) (ans += f[i]) %= mod;
		printf("%lld\n", ans);
	}
	return 0;
}

I 超消函数

题意

给定一个长度为 n n n的排列,每次可以选择两个数 a , b a,b a,b,删除这两个数,加入 g c d ( a , b ) gcd(a,b) gcd(a,b),并把 g c d ( a , b ) gcd(a,b) gcd(a,b)累加到 s u m sum sum中,要求最后只剩一个数。求 s u m sum sum的最大值。

思路

考虑 g c d ( a , b ) gcd(a,b) gcd(a,b),设 a = 2 p 1 ∗ 3 p 2 ∗ ⋯   , b = 2 p 1 ′ ∗ 3 p 2 ′ ∗ ⋯ a=2^{p_1}*3^{p_2}*\cdots,b=2^{p_1'}*3^{p_2'}*\cdots a=2p13p2,b=2p13p2 g c d ( a , b ) = 2 m i n { p 1 , p 1 ′ } ∗ 3 m i n { p 2 , p 2 ′ } ∗ ⋯ gcd(a,b)=2^{min\{p_1,p_1'\}}*3^{min\{p_2,p_2'\}}*\cdots gcd(a,b)=2min{p1,p1}3min{p2,p2}
对于两个数 a , b a,b a,b,不妨令 a > b a>b a>b,有 g c d ( a , b ) ≤ min ⁡ { a , b } gcd(a,b) \leq \min\{a,b\} gcd(a,b)min{a,b},当 b b b a a a的因子时取等号,即能取到最大值 b b b。所以,当 a a a固定时, b b b a a a的最大因子时 g c d ( a , b ) gcd(a,b) gcd(a,b)有最大值,即 b = a / m p r i [ a ] b=a/mpri[a] b=a/mpri[a],其中 m p r i [ a ] mpri[a] mpri[a]表示 a a a的最小质因子。
考虑最大的数为 n n n,令另外一个数 m = n / m p r i [ n ] m=n/mpri[n] m=n/mpri[n]时,此时 g c d ( n , m ) gcd(n,m) gcd(n,m)有最大值 m m m,删除 n , m n,m n,m后新加入的数为 m m m,相当于只删除了 n n n,现在问题变成长度为 n − 1 n-1 n1的子问题,重复以上操作即可。
m m m不取 n / m p r i [ n ] n/mpri[n] n/mpri[n]有没有可能更优?假设 m ≠ n / m p r i [ n ] m \neq n/mpri[n] m=n/mpri[n],有两种情况:

  1. m m m n n n的因子,此时 g c d ( n , m ) < g c d ( n , n / m p r i [ n ] ) gcd(n,m) < gcd(n,n/mpri[n]) gcd(n,m)<gcd(n,n/mpri[n]),且执行完删数、加数后是长度为 n − 1 n-1 n1的子问题,不可能更优。
  2. m m m不为 n n n的因子,同样有 g c d ( n , m ) < g c d ( n , n / m p r i [ n ] ) gcd(n,m) < gcd(n,n/mpri[n]) gcd(n,m)<gcd(n,n/mpri[n]),但删除完 n , m n,m n,m后增加了一个小于 m m m的数,设其为 k k k,此时 k k k m m m的因子, k k k与其他数的 g c d gcd gcd值不会超过 m m m与其他数的 g c d gcd gcd值,即不可能更优。

所以答案为 ∑ i = 1 n i / m p r i [ i ] \sum\limits_{i=1}^ni/mpri[i] i=1ni/mpri[i],其中 m p r i [ i ] mpri[i] mpri[i] i i i的最小质因子。 m p r i [ i ] mpri[i] mpri[i]用任意质数筛求即可,下面代码用的是线性质数筛。

代码

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

#define fo(i, x, y) for (int i = (x); i <= (y); ++i)
#define fd(i, x, y) for (int i = (x); i >= (y); --i)

typedef long long ll;

const int maxn = 1e4 + 5;

int n, m;
int g[maxn], pri[maxn];

int getint()
{
	char ch;
	int res = 0, p;
	while (!isdigit(ch = getchar()) && (ch ^ '-'));
	p = ch == '-'? ch = getchar(), -1 : 1;
	while (isdigit(ch))
		res = (res << 3) + (res << 1) + (ch ^ 48), ch = getchar();
	return res * p;
}

int main()
{
	int T = getint();
	while (T--)
	{
		n = getint();
		fo(i, 1, n) g[i] = 0;
		m = 0;
		ll ans = 0;
		fo(i, 2, n)
		{
			if (!g[i]) g[i] = i, pri[++m] = i;
			fo(j, 1, m)
			{
				if (pri[j] > g[i] || i > n / pri[j]) break;
				g[i * pri[j]] = pri[j];
			}
			ans += i / g[i];
		}
		printf("%lld\n", ans);
	}
	return 0;
}

J 最大公因数排序

题意

给定长度为 n n n的数列 { a i } \{a_i\} {ai},令 b i = 2 a i b_i=2^{a_i} bi=2ai,若 g c d ( b i , b j ) = m i n { b i } ( i ≠ j ) gcd(b_i,b_j)=min\{b_i\}(i \neq j) gcd(bi,bj)=min{bi}(i=j),则可以交换 b i b_i bi b j b_j bj,问能否将 { b i } \{b_i\} {bi}变为有序。

思路

首先 g c d ( b i , b j ) = g c d ( 2 a i , 2 a j ) = 2 m i n { a i , a j } gcd(b_i,b_j)=gcd(2^{a_i},2^{a_j})=2^{min\{a_i,a_j\}} gcd(bi,bj)=gcd(2ai,2aj)=2min{ai,aj} g c d ( b i , b j ) = m i n { b i } ( i ≠ j ) gcd(b_i,b_j)=min\{b_i\}(i \neq j) gcd(bi,bj)=min{bi}(i=j)等价于 2 m i n { a i , a j } = 2 m i n { a i } 2^{min\{a_i,a_j\}}=2^{min\{a_i\}} 2min{ai,aj}=2min{ai},即 m i n { a i , a j } = m i n { a i } min\{a_i,a_j\}=min\{a_i\} min{ai,aj}=min{ai}
问题可以转化为对于 i ≠ j i \neq j i=j a i a_i ai a j a_j aj中存在 { a i } \{a_i\} {ai}的最小值,则可以交换 a i , a j a_i,a_j ai,aj。问最后否将 { a i } \{a_i\} {ai}变为有序。显然最小值是可以自由移动的,可以借助最小值将 { a i } \{a_i\} {ai}变为有序,不清楚的可以手动模拟一下。所以答案恒为"Yes"。

代码

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

#define fo(i, x, y) for (int i = (x); i <= (y); ++i)
#define fd(i, x, y) for (int i = (x); i >= (y); --i)

const int maxn = 1e5 + 5;

int n;
int a[maxn];

int getint()
{
	char ch;
	int res = 0, p;
	while (!isdigit(ch = getchar()) && (ch ^ '-'));
	p = ch == '-'? ch = getchar(), -1 : 1;
	while (isdigit(ch))
		res = (res << 3) + (res << 1) + (ch ^ 48), ch = getchar();
	return res * p;
}

int main()
{
	int T;
	T = getint();
	while (T--)
	{
		n = getint();
		fo(i, 1, n) a[i] = getint();
		printf("Yes\n"); 
	}
	return 0;
}
  • 10
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值