Codeforces Round #641 (Div. 2)(A,B,C,D,E)

Codeforces Round #641 Div. 2

  • A. Orac and Factors
  • B. Orac and Models
  • C. Orac and LCM
  • D. Orac and Medians
  • E. Orac and Game of Life

比赛的时候才开出两题T~T,赛后想想,被C题的数论唬到了,第一时间自己认为自己做不了数论题,其实完全不难。

A. Orac and Factors

思路:
就是找出这个数的最小的因子,加一次,然后剩下的 k − 1 k-1 k1次都是加2,
(这里我用的质数筛法求出的每个数的最小的因子, O ( n l o g ( n ) + t ) O(nlog(n) + t) O(nlog(n)+t), 其实完全不用这么麻烦,直接从小大到枚举出最小的因子也是可以的,时间复杂度为 O ( t n ) O(t\sqrt{n}) O(tn )

代码:

#include 
using namespace std;
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define fi first
#define se second
typedef vector<int> vi;
typedef queue<int> qi;
typedef long long ll;
typedef pair<int, int> pii;
typedef double db;
const int N = 1e6 + 5;
int fac[N];
bool prim[N];

void get_prime(int x)
{
	for (int i = 2; i * i <= x; ++ i)
		if(!prim[i])
			for (int j = i * i; j <= x; j += i)
			{
				prim[j] = 1;
				if(!fac[j]) fac[j] = i;
			}
}
int main()
{
	get_prime(1e6);
	int t; cin >> t;
	while (t --)
	{
		ll n, k; cin >> n >> k;
		ll ans = n;
		if(!prim[n]) ans += n, -- k;
		else ans += fac[n], -- k;
		ans += k * 2;
		cout << ans << endl;
	}

	return 0;
}

B. Orac and Models

思路:
其实差不多就是最长上升子序列,就是有一点限制条件,第 i i i项只能接在第 j j j项的后面, j j j必须满足 j < i    & &    i % j = = 0 j < i \;\&\&\; i \% j == 0 j<i&&i%j==0
O ( n ) O(\sqrt{n}) O(n )的时间内枚举 a [ i ] a[i] a[i]的约数即可,总的时间复杂度为 O ( n n ) O(n\sqrt{n}) O(nn )

代码:

#include 
using namespace std;
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define fi first
#define se second
typedef vector<int> vi;
typedef queue<int> qi;
typedef long long ll;
typedef pair<int, int> pii;
typedef double db;
const int N = 1e5 + 5;
int dp[N], a[N];

int main()
{
	int t; scanf("%d", &t);
	while (t --)
	{
		int n; scanf("%d", &n);
		for (int i = 1; i <= n; ++ i)
		{
			dp[i] = 1;
			scanf("%d", &a[i]);
			for (int j = 1; j * j <= i; ++ j)
			{
				if(i % j == 0)
				{
					if(a[i] > a[j]) dp[i] = max(dp[i], dp[j] + 1);
					if(a[i] > a[i / j]) dp[i] = max(dp[i], dp[i / j] + 1);
				}
			}
		}
//		for (int i = 1; i <= n; ++ i) cout << dp[i] << " "; cout << endl;
		int ans = 0;
		for (int i = 1; i <= n; ++ i) ans = max(ans, dp[i]);
		printf("%d\n", ans);
	}

	return 0;
}

C. Orac and LCM

思路:
对于这类数论题,可以都先分解质因子,看看有没有啥规律,
4 10 24 40 80 4 \\ 10\quad24\quad 40\quad 80 410244080
质因子分解
10 : 2 1 ∗ 5 1 10 : 2^1*5^1 10:2151
24 : 2 3 ∗ 5 0 24 : 2^3*5^0 24:2350
40 : 2 3 ∗ 5 1 40 : 2^3*5^1 40:2351
80 : 2 4 ∗ 5 1 80 : 2^4*5^1 80:2451
a n s = 40 : 2 3 ∗ 5 1 ans = 40 : 2^3*5^1 ans=40:2351
观察一下可以发现,答案就是质因子的次小次方的累乘,比如说质因子 2 : { 1 , 3 , 3 , 4 } 2:\{1,3,3,4\} 2:{1,3,3,4},质因子 5 : { 0 , 1 , 1 , 1 } 5:\{0,1,1,1\} 5:{0,1,1,1},他们的次小次方就是 2 3 , 5 1 2^3,5^1 23,51,因此 a n s = 2 3 ∗ 5 1 = 40 ans = 2^3*5^1=40 ans=2351=40
这么做也是符合理论要求的,lcm对质因子p来说就是 p m a x ( c ) p^{max(c)} pmax(c),gcd对于质因子p来说就是 p m i n ( c ) p^{min(c)} pmin(c),因此 m i n ( c ) min(c) min(c)就是由最小的两个 c i , c j c_i,c_j ci,cj决定的, m i n ( c ) min(c) min(c)具体也就是等于次小的指数.

但是这里不能算出所有的质因子,然后对于每个数,遍历所有的质因子,会TLE,因此对于每个数分解质因子,同时记录每个质因子在每个数中是否出现过,
对于质因子p

  1. 若p存在于n-1个数中,则就是n-1个数中,最小的指数(因为有一个数没有质因子p,所以它的指数为0,必定是最小的了)
  2. 若p存在于n个数中,则就是n个数中,次小的指数
  3. 其他情况都不用计算了,答案不包含质因子p了

代码:

#include 
using namespace std;
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define fi first
#define se second
typedef vector<int> vi;
typedef queue<int> qi;
typedef long long ll;
typedef pair<int, int> pii;
typedef double db;
const int N = 1e5 + 5;
int a[N];
int mn[2 * N][3];
int ct[N * 2];

ll Pow(ll a, ll b)
{
	ll res = 1, tmp = a;
	for (; b; b >>= 1)
	{
		if(b & 1) res = res * tmp;
		tmp = tmp * tmp;
	}
	return res;
}
int main()
{
	int n; cin >> n;
	int mx = 0;
	for (int i = 1; i <= n; ++ i)
	{
		cin >> a[i];
		mx = max(mx, a[i]);
	}
	for (int i = 1; i <= mx; ++ i) ct[i] = n;
	memset(mn, 0x3f, sizeof(mn));
	ll ans = 1;
	for (int i = 1; i <= n; ++ i)
	{
		for (int j = 2; j * j <= a[i]; ++ j)
		{
			if(a[i] % j == 0)
			{
				int cnt = 0;
				while (a[i] % j == 0) a[i] /= j, cnt ++;
				ct[j] --;
				if(cnt < mn[j][0]) mn[j][1] = mn[j][0], mn[j][0] = cnt;
				else if(cnt < mn[j][1]) mn[j][1] = cnt; 
			}
		}
		if(a[i] > 1)
		{
			ct[a[i]] --;
			if(1 < mn[a[i]][0]) mn[a[i]][1] = mn[a[i]][0], mn[a[i]][0] = 1;
			else if(1 < mn[a[i]][1]) mn[a[i]][1] = 1; 
		}
	}
	for (int i = 2; i <= mx; ++ i)
	{
//		cout << i << ": " << ct[i] << " " << mn[i][1] << endl;
		if(ct[i] == 1) ans = ans * Pow(i, mn[i][0]);
		else if(ct[i] == 0) ans = ans * Pow(i, mn[i][1]);
	}
	cout << ans << endl;	
	return 0;
}

D. Orac and Medians

思路:
首先对数进行处理,大于k的都看作1,小于k的都看作-1,等于k的都看作0,很显然这样更改数据时没有问题的,
性质1: 存在{0,0}, 那就一定可以使得整个序列全部是0,很明显吧
性质2: 存在{0,1},或者{1,0},就一定可以变成{0,0},则根据性质1,一定可以使整个序列全部变成0,
性质3:{-1,0}并不能使的整个序列全部成为0.

最关键的一点来了
通过性质2,和性质3,可以看出1比-1要好得多,可不可以多一些1,少一些-1呢,(反正我比赛的时候是没想不出来这么巧妙的性质),我们可以想办法将除0以外的所有-1都变成1,然这样就肯定可以存在{0,1},使得又满足性质2了,
因此只要存在一段区间,使得1出现得次数 c t 1 ct_1 ct1大于-1出现得次数 c t − 1 ct_{-1} ct1,就一定可以使整个区间变成1,进而构造出性质2,进而整个序列合法

另外还有这样得情况 0 , − 1 , 1 0,-1,1 0,1,1,也是合法得,这样我们可以把0也看做成1,然后也符合上面得情况。

因此整个序列就成为1和-1构成得序列,只要存在一段区间1得数量大于-1得数量就是YES,问题就成为经典问题最大连续和 ,若序列最大连续和(长度必须大于1)大于0,就是YES,
特判只有一个k得情况

代码:

#include 
using namespace std;
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define fi first
#define se second
typedef vector<int> vi;
typedef queue<int> qi;
typedef long long ll;
typedef pair<int, int> pii;
typedef double db;
const int N = 1e5 + 5;
int a[N];

int main()
{
	int t; scanf("%d", &t);
	while (t --)
	{
		int n, k; scanf("%d%d", &n, &k);
		int f = 0;
		for (int i = 1, j; i <= n; ++ i)
		{
			scanf("%d", &j);
			if(j == k && !f) a[i] = 1, f = 1;
			else if(j >= k) a[i] = 1;
			else a[i] = -1;
		}
		if(!f)
		{
			puts("no");
			continue;	
		}
		if(n == 1 && f)
		{
			puts("yes");
			continue;
		}
		int res = 0, ans = 0;
		f = 0;
		for (int i = 1; i <= n; ++ i)
		{
			res += a[i], f ++;
			if(f > 1) ans = max(ans, res);
			if(res < 0) res = 0, f = 0;
		}
		if(ans > 0) puts("yes");
		else puts("no");
	}
	return 0;
}

E. Orac and Game of Life

思路:
简单过前面两题。。。
性质1:若一个格子的周围有和它一样颜色的格子,则就会一直闪

每个格子都会被最近的闪烁格子给影响,只是传递需要时间,而被带着一起闪,(画一下样例3,可以发现最后所有格子的颜色都会一致,)因此从闪烁点出发,跑bfs,统计每个点最小的被影响的时间。

代码:

#include 
using namespace std;
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define fi first
#define se second
typedef vector<int> vi;
typedef queue<int> qi;
typedef long long ll;
typedef pair<int, int> pii;
typedef double db;
const int N = 1e3 + 5;
const int dir[4][2] = {-1, 0, 0, 1, 1, 0, 0, -1};
char s[N][N];
int d[N][N];
bool v[N][N];
int n, m, t;

inline bool valid(int x, int y)
{
	if(x >= 1 && x <= n && y >= 1 && y <= m) return 1;
	else return 0;
}
int main()
{
	scanf("%d%d%d", &n, &m, &t);
	for (int i = 1; i <= n; ++ i) scanf("%s", s[i] + 1);
	
	memset(d, 0x3f, sizeof(d));
	queue<pair<int, int> > q;
	for (int i = 1; i <= n; ++ i)
		for (int j = 1; j <= m; ++ j)
		{
			for (int k = 0; k < 4; ++ k)
			{
				int tx = i + dir[k][0], ty = j + dir[k][1];
				if(valid(tx, ty) && s[tx][ty] == s[i][j])
				{
					d[i][j] = 0;
					v[i][j] = 1;
					q.push(mp(i, j));
				} 
			}
		} 
	while (q.size())
	{
		int x = q.front().fi, y = q.front().se; q.pop();
		v[x][y] = 0;
		for (int k = 0; k < 4; ++ k)
		{
			int tx = x + dir[k][0], ty = y + dir[k][1];
			if(valid(tx, ty) && d[tx][ty] > d[x][y] + 1)
			{
				d[tx][ty] = d[x][y] + 1;
				if(!v[tx][ty]) q.push(mp(tx, ty)), v[tx][ty] = 1;
			}
		}
	}
	while (t --)
	{
		int x, y;
		ll p; scanf("%d%d%lld", &x, &y, &p);
		int val;
		if(d[x][y] == 0x3f3f3f3f || p <= d[x][y]) val = s[x][y] - 48;
		else if(p > d[x][y])
			if((p - d[x][y]) % 2) val = 1 - (s[x][y] - 48);
			else val = (s[x][y] - 48);
		printf("%d\n", val);
	}
	return 0;
}

你可能感兴趣的:(CodeForces)