Codeforces Round #647 (Div.2)

比赛链接 / 官方题解链接

A. Johnny and Ancient Computer

Solution

转换成对大的那个数不断做除法。

Code

>folded
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include<bits/stdc++.h>

using namespace std;

template<typename T>void read(T&x){x=0;int fl=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')
fl=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}x*=fl;}
template<typename T,typename...Args>inline void read(T&t,Args&...args){read(t);read(args...);}

typedef long long LL;
typedef vector<int> vi;
typedef pair<int, int> pii;
#define mp(x, y) make_pair(x, y)
#define pb(x) emplace_back(x)

int T;
LL a, b;

int main(){
for(read(T); T; T--){
read(a, b);
int cnt = 0;
while(a != b){
if(a > b) swap(a, b);
if(b % 8 == 0) b /= 8, cnt++;
else if(b % 4 == 0) b /= 4, cnt++;
else if(b % 2 == 0) b /= 2, cnt++;
else break;
if(b * 2 == a) b *= 2;
else if(b * 4 == a) b *= 4;
}
if(a == b) printf("%d\n", cnt);
else puts("-1");
}
return 0;
}

B. Johnny and His Hobbies

Solution

枚举 $k$ 验证即可。

Code

>folded
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include<bits/stdc++.h>

using namespace std;

template<typename T>void read(T&x){x=0;int fl=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')
fl=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}x*=fl;}
template<typename T,typename...Args>inline void read(T&t,Args&...args){read(t);read(args...);}

typedef long long LL;
typedef vector<int> vi;
typedef pair<int, int> pii;
#define mp(x, y) make_pair(x, y)
#define pb(x) emplace_back(x)

int T, n;
set<int> s;

inline bool check(int x){
set<int> t;
for(auto k: s)
t.insert(k ^ x);
return s == t;
}

int main(){
for(read(T); T; T--){
read(n);
bool ok = false;
s.clear();
for(int i = 1; i <= n; i++){
int x; read(x);
s.insert(x);
}
for(int i = 1; i <= 1024; i++){
if(check(i)){
printf("%d\n", i);
ok = true;
break;
}
}
if(!ok) puts("-1");
}
return 0;
}

C. Johnny and Another Rating Drop

Solution

一位一位地考虑,第 $i$ 位是连续 $2^i$ 个 $0$,然后连续 $2^i$ 个 $1$,然后再连续 $2^i$ 个 $0$ ……如此反复。每个 $0,1$ 转换的地方对答案有贡献,所以第 $i$ 对答案的贡献就是 $\left\lceil\frac{n+1}{2^i}\right\rceil-1$.

Code

>folded
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include<bits/stdc++.h>

using namespace std;

template<typename T>void read(T&x){x=0;int fl=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')
fl=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}x*=fl;}
template<typename T,typename...Args>inline void read(T&t,Args&...args){read(t);read(args...);}

typedef long long LL;
typedef vector<int> vi;
typedef pair<int, int> pii;
#define mp(x, y) make_pair(x, y)
#define pb(x) emplace_back(x)

int T;
LL n;

int main(){
for(read(T); T; T--){
read(n);
LL ans = 0;
for(int i = 0; i <= 60; i++){
if((1ll << i) > n) break;
ans += (n + 1) / (1ll << i) - 1;
ans += ((n + 1) % (1ll << i) != 0);
}
printf("%lld\n", ans);
}
return 0;
}

D. Johnny and Contribution

Solution

如果给定的图合法,那么排序后输出就行了。所以我们只需要判断给定的图是否合法,也就是是否对于每个点,所有小于这个点的值都出现在与之相邻的点中,且不存在与之相等的点。那只需要把小于这个点的值都加起来,看是否等于 $\frac{t_i\cdot(t_i-1)}{2}$.

Code

>folded
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include<bits/stdc++.h>

using namespace std;

template<typename T>void read(T&x){x=0;int fl=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')
fl=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}x*=fl;}
template<typename T,typename...Args>inline void read(T&t,Args&...args){read(t);read(args...);}

typedef long long LL;
typedef vector<int> vi;
typedef pair<int, int> pii;
#define mp(x, y) make_pair(x, y)
#define pb(x) emplace_back(x)

const int N = 500005;

int n, m;
vi edge[N];
bool b[N];

struct Node{
int t, id;
bool operator < (const Node &A) const{
return t < A.t;
}
}a[N];

int main(){
read(n, m);
for(int i = 1; i <= m; i++){
int u, v; read(u, v);
edge[u].pb(v), edge[v].pb(u);
}
for(int i = 1; i <= n; i++) read(a[i].t), a[i].id = i;
for(int i = 1; i <= n; i++){
LL sum = 0;
for(auto to: edge[i]){
if(a[to].t == a[i].t) return puts("-1"), 0;
if(a[to].t < a[i].t && !b[a[to].t]){
b[a[to].t] = true;
sum += a[to].t;
}
}
if(sum != 1ll * a[i].t * (a[i].t - 1) / 2)
return puts("-1"), 0;
for(auto to: edge[i])
if(a[to].t < a[i].t)
b[a[to].t] = false;
}
sort(a+1, a+n+1);
for(int i = 1; i <= n; i++)
printf("%d ", a[i].id);
return 0;
}

E. Johnny and Grandmaster

Solution

从大到小对 $k$ 排序,显然最优策略是:把最大的放进 $A$ 集合,然后不断放 $B$ 集合直到其和与 $A$ 集合相等,然后重复这个过程。由于取模的存在,我们存两集合的差值——放进 $A$ 集合就是加,放进 $B$ 集合就是减,同时为了避免取模后变成 $0$,再选取一个模数,类似双哈希。

P.S. 像 $998244353$ 这个常见的模数被卡了……所以还是用个不常见的质数好了。

Code

>folded
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include<bits/stdc++.h>

using namespace std;

template<typename T>void read(T&x){x=0;int fl=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')
fl=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}x*=fl;}
template<typename T,typename...Args>inline void read(T&t,Args&...args){read(t);read(args...);}

typedef long long LL;
typedef vector<int> vi;
typedef pair<int, int> pii;
#define mp(x, y) make_pair(x, y)
#define pb(x) emplace_back(x)

const int N = 1000005;
const LL mod1 = 1e9 + 7;
const LL mod2 = 10000019;

int T, n;
LL p, k[N];

LL fpow(LL bs, LL idx, LL MOD){
bs %= MOD;
LL res = 1;
while(idx){
if(idx & 1) (res *= bs) %= MOD;
(bs *= bs) %= MOD;
idx >>= 1;
}
return res;
}

int main(){
for(LL i = 2; i * i <= mod2; i++)
if(mod2 % i == 0)
printf("%lld\n", i);
for(read(T); T; T--){
read(n, p);
for(int i = 1; i <= n; i++) read(k[i]);
sort(k+1, k+n+1), reverse(k+1, k+n+1);
LL ans = 0, diff = 0;
for(int i = 1; i <= n; i++){
if(ans == 0 && diff == 0){
ans += fpow(p, k[i], mod1);
diff += fpow(p, k[i], mod2);
}
else{
(((ans -= fpow(p, k[i], mod1)) %= mod1) += mod1) %= mod1;
(((diff -= fpow(p, k[i], mod2)) %= mod2) += mod2) %= mod2;
}
}
printf("%lld\n", ans);
}
return 0;
}

F. Johnny and Megan’s Necklace

Solution

很有趣的一道题!从大到小枚举答案 $k$,那么两个数字能够被“粘合”当且仅当它们的末 $k$ 位相同,所以对于端点值为 $a,b$ 的边,我们在 $a\text{ AND }(2^k-1)$ 和 $b\text{ AND }(2^k-1)$ 之间连一条边,那么从一条边经过一个点走到另一条边,就相当于粘合了两段项链,所以问题转化成了找一条欧拉回路!

找欧拉回路的算法叫做 $Hierholzer$ 算法,基本过程就是 $dfs$,然后用一个栈把欧拉路/回路记录下来。

Code

>folded
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#include<bits/stdc++.h>

using namespace std;

template<typename T>void read(T&x){x=0;int fl=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')
fl=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}x*=fl;}
template<typename T,typename...Args>inline void read(T&t,Args&...args){read(t);read(args...);}

typedef long long LL;
typedef vector<int> vi;
typedef pair<int, int> pii;
#define mp(x, y) make_pair(x, y)
#define pb(x) emplace_back(x)

const int N = (1 << 20) + 5;

int n;
struct Node{
int a, b;
}a[N];
vector<pii> edge[N];

bool vis[N];
void dfs(int x){
vis[x] = true;
for(auto &to: edge[x]) if(!vis[to.first]) dfs(to.first);
}

stack<int> sta;
void dfs(int x, int pre){
while(!edge[x].empty()){
pii to = edge[x].back(); edge[x].pop_back();
if(vis[(to.second+1)/2]) continue;
vis[(to.second+1)/2] = true;
dfs(to.first, to.second);
}
if(pre != -1){
sta.push(pre);
if(pre & 1) sta.push(pre + 1);
else sta.push(pre - 1);
}
}

inline bool check(int B){
for(int i = 0; i <= B; i++) edge[i].clear(), vis[i] = false;
for(int i = 1; i <= n; i++){
edge[a[i].a & B].pb(mp(a[i].b & B, 2 * i));
edge[a[i].b & B].pb(mp(a[i].a & B, 2 * i - 1));
}
for(int i = 1; i <= n; i++){
if(edge[a[i].a & B].size() & 1) return false;
if(edge[a[i].b & B].size() & 1) return false;
}

int comps = 0;
for(int i = 0; i <= B; i++)
if(!vis[i] && !edge[i].empty())
dfs(i), comps++;
if(comps != 1) return false;

for(int i = 1; i <= n; i++) vis[i] = false;
for(int i = 0; i <= B; i++)
if(!edge[i].empty())
dfs(i, -1);
return true;
}

int main(){
read(n);
for(int i = 1; i <= n; i++) read(a[i].a, a[i].b);
for(int b = 20; b >= 0; b--){
if(check((1<<b)-1)){
printf("%d\n", b);
break;
}
}
while(!sta.empty()){
printf("%d ", sta.top());
sta.pop();
}
return 0;
}
作者

xyfJASON

发布于

2020-06-06

更新于

2020-12-20

许可协议

评论