Codeforces Round694 Div2
技術標籤:Codeforces數學圖
Codeforces Round694 Div2
D Strange Definition
D題沒做出來。題意也比較複雜,n個數
a
1
,
a
2
,
⋯
,
a
n
a_1, a_2, \cdots, a_n
a1,a2,⋯,an。先上一個定義,如果正整數x和y滿足,
l
c
m
(
x
,
y
)
g
c
d
(
x
,
y
)
\frac{lcm(x,y)}{gcd(x,y)}
gcd(x,y)lcm(x,y)是完全平方數,那麼x和y鄰近。每一時刻,
a
i
a_i
ai會變成陣列中其他所有和它鄰近的元素之積(包括自己,如果自己和自己鄰近)。有
q
q
樣例輸入:
2
4
6 8 4 2
1
0
6
12 3 20 5 80 1
1
1
樣例輸出:
2
3
比如樣例,{6,8,4,2}裡,8和8,2相鄰所以最大是2。第二個樣例,1秒後,陣列變成{36,36,8000,8000,8000,1},d最大的是8000,是3。
首先肯定是化簡鄰近的意義,設 g c d ( x , y ) = k gcd(x,y)=k gcd(x,y)=k,那麼 l c m ( x , y ) g c d ( x , y ) = x y k 2 \frac{lcm(x,y)}{gcd(x,y)}=\frac{xy}{k^2} gcd(x,y)lcm(x,y)=k2xy,開根號之後說明xy要是完全平方數。想了一會會不會 x y \sqrt{xy} xy 不整除k,答案是不可能,因為x和y都整除k。所以鄰近等價於 x y xy xy是完全平方數。
比賽時想到這個,後來就沒什麼辦法了。其實需要繼續挖掘這個條件,將x和y質因數分解之後,相乘如果是完全平方數,肯定是相乘之後所有質因數的次數都是偶數。換言之,對於一個數
a
i
a_i
所以經過這個處理, a a a陣列變成等價之後的結果, w = 0 w=0 w=0時最大的d,其實就是等價之後出現次數最多的數。那 w > 0 w>0 w>0怎麼辦?
我們看第一秒。第一秒等價於,比方說 b i b_i bi是化簡後的值, a i a_i ai會變成數組裡所有的 b i b_i bi的乘積。比如說第二個樣例,化簡之後陣列變成{3,3,5,5,5,1},第一秒後變成 { 3 2 , 3 2 , 5 3 , 5 3 , 5 3 , 1 } \{3^2,3^2,5^3,5^3,5^3,1\} {32,32,53,53,53,1},這個時候,如果一個等價的集合有偶數個,比如3,那麼和他鄰近的數有所有其他的偶數冪,以及1(不能忘了1)。而對於一個奇數次冪,和他鄰近的,只有值和他自己相等的數。比如這裡, 3 2 3^2 32的d是3, 5 3 5^3 53的d也是3。所以 w = 1 w=1 w=1時候的思路是,比較最大的奇數次冪 個數,和所有偶數次冪和1加起來的次數,取max值,就是新的 d m a x d_{max} dmax。
那接下來怎麼辦?我們繼續看 w = 2 w=2 w=2,可以發現乘了之後,樣例2變成了 { 3 4 , 3 4 , 5 9 , 5 9 , 5 9 , 3 4 } \{3^4,3^4,5^9,5^9,5^9,3^4\} {34,34,59,59,59,34},所有的偶數次冪和1合併成了一個群體,而奇數次冪仍然不變。偶數次冪合併之後還是偶數次冪,奇數次冪演變之後還是奇數次冪,所以接下來的變化還是偶數次冪合併,奇數次冪只乘和自己等的冪。所以接下來的d永遠不會變化。這也是為什麼 w w w這麼大。程式碼如下:
- 質因數分解的技巧,先預處理每個數的minFactor
- mp儲存每個 a i a_i ai等價之後的結果,用奇數次冪的質因數之積表示
- 答案分為兩種,一種是w=0,一種是w>0
- 突然發現even和odd好像寫反了。。
//
// main.cpp
//
//
// Created by ji luyang on 2020/12/22.
//
#include <stdio.h>
#include <iostream>
#include <fstream>
#include <string>
#include <cstring>
#include <time.h>
#include <algorithm>
#include <vector>
#include <queue>
#include <map>
using namespace std;
int t;
int a[300010], minFactor[1000010];
int main() {
memset(minFactor, 0, sizeof(minFactor));
for (int i = 2; i <= 1e6; i++) {
if (minFactor[i] == 0) {
minFactor[i] = i;
for (int j = 2 * i; j <= 1e6; j += i) {
if (minFactor[j] == 0) {
minFactor[j] = i;
}
}
}
}
cin >> t;
while (t--) {
int n, q;
cin >> n;
map<int, int> mp;
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
int x = a[i], y = 1;
while (x > 1) {
int minp = minFactor[x], cnt = 0;
while (x % minp == 0) {
x /= minp;
cnt++;
}
if (cnt % 2) {
y *= minp;
}
}
mp[y]++;
}
int res0 = 0, res1 = 0, cntodd = 0, maxeven = 0;
for (auto it: mp) {
res0 = max(res0, it.second);
if (it.first == 1 || it.second % 2 == 0) {
cntodd += it.second;
} else {
maxeven = max(maxeven, it.second);
}
}
res1 = max(cntodd, maxeven);
cin >> q;
while (q--) {
long long w;
scanf("%lld", &w);
if (!w) printf("%d\n", res0);
else printf("%d\n", res1);
}
}
return 0;
}
F Strange Housing
n個點,m條邊。老師們要住在一些點裡,要滿足一些條件:
- 如果一條邊的兩個頂點都沒有老師,這條邊會刪掉
- 刪掉邊之後,整個圖要是聯通的
- 老師不能住在一條邊的兩個頂點
n是 1 0 5 10^5 105級別,m也是。計算是否存在這樣的點集?如果存在,輸出任意的點集。
樣例輸入:
2
3 2
3 2
2 1
4 2
1 4
2 3
樣例輸出:
YES
2
1 3
NO
樣例輸入:
1
17 27
1 8
2 9
3 10
4 11
5 12
6 13
7 14
8 9
8 14
8 15
9 10
9 15
10 11
10 15
10 17
11 12
11 17
12 13
12 16
12 17
13 14
13 16
14 16
14 15
15 16
15 17
16 17
樣例輸出:
YES
8
1 3 4 5 6 9 14 17
其實就是染色問題。比如從點1開始,染成黑色,那麼其他和它相連的點都得染成白色,即黑色不能相連。也不能有一個白點連的所有點都是白色,否則這個點將不可達。所以邊dfs邊染色,最後的黑色點集即為所求:
- 對於一個點,先檢查是否連有黑點。如果有,一定染成白色;如果沒有,那就染成黑色
- 要注意一個問題:這裡如果每個t內,用memset去初始化color會超時。memset也是要對這麼多位去置0的,有的樣例t很大,但是每個樣例內的m和n很小,這種情況就會超時。
//
// main.cpp
//
//
// Created by ji luyang on 2020/12/22.
//
#include <stdio.h>
#include <iostream>
#include <fstream>
#include <string>
#include <cstring>
#include <time.h>
#include <algorithm>
#include <vector>
#include <queue>
#include <map>
using namespace std;
int t;
vector<int> g[300010];
int n, m;
int color[300010];
void dfs(int u) {
int flag = 1;
for (int v: g[u]) {
if (color[v] == 1) {
flag = 0;
break;
}
}
if (flag) color[u] = 1;
else color[u] = -1;
for (int v: g[u]) {
if (color[v] == 0) {
dfs(v);
}
}
}
int main() {
cin >> t;
while (t--) {
for (int i = 1; i <= n; i++) {
g[i].clear();
color[i] = 0;
}
scanf("%d %d", &n, &m);
for (int i = 0; i < m; i++) {
int u, v;
scanf("%d %d", &u, &v);
g[u].push_back(v);
g[v].push_back(u);
}
dfs(1);
int ifconnect = 1, sum = 0;
for (int i = 1; i <= n; i++) {
if (!color[i]) ifconnect = 0;
if (color[i] == 1) sum++;
}
if (!ifconnect) printf("NO\n");
else {
printf("YES\n");
printf("%d\n", sum);
for (int i = 1; i <= n; i++) {
if (color[i] == 1) {
printf("%d ", i);
}
}
printf("\n");
}
}
return 0;
}