Codeforces Round #641 Div. 2
比赛的时候才开出两题T~T,赛后想想,被C题的数论唬到了,第一时间自己认为自己做不了数论题,其实完全不难。
A. Orac and Factors
思路:
就是找出这个数的最小的因子,加一次,然后剩下的
k
−
1
k-1
k−1次都是加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 <bits/stdc++.h>
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 <bits/stdc++.h>
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:21∗51
24
:
2
3
∗
5
0
24 : 2^3*5^0
24:23∗50
40
:
2
3
∗
5
1
40 : 2^3*5^1
40:23∗51
80
:
2
4
∗
5
1
80 : 2^4*5^1
80:24∗51
a
n
s
=
40
:
2
3
∗
5
1
ans = 40 : 2^3*5^1
ans=40:23∗51
观察一下可以发现,答案就是质因子的次小次方的累乘,比如说质因子
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=23∗51=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
- 若p存在于n-1个数中,则就是n-1个数中,最小的指数(因为有一个数没有质因子p,所以它的指数为0,必定是最小的了)
- 若p存在于n个数中,则就是n个数中,次小的指数
- 其他情况都不用计算了,答案不包含质因子p了
代码:
#include <bits/stdc++.h>
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}
ct−1,就一定可以使整个区间变成1,进而构造出性质2,进而整个序列合法。
另外还有这样得情况 0 , − 1 , 1 0,-1,1 0,−1,1,也是合法得,这样我们可以把0也看做成1,然后也符合上面得情况。
因此整个序列就成为1和-1构成得序列,只要存在一段区间1得数量大于-1得数量就是YES,问题就成为经典问题最大连续和 ,若序列最大连续和(长度必须大于1)大于0,就是YES,
特判只有一个k得情况
代码:
#include <bits/stdc++.h>
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 <bits/stdc++.h>
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;
}