Codeforces Round 943 (Div. 3) 赛后总结

比赛链接

我服了做得太烂了。。。

还差36分变绿,死一会儿。。。

题解

A. Maximize?

典,暴力枚举即可

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
#include<bits/stdc++.h>

using namespace std;

int t,x;

int main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);

cin>>t;
while(t--)
{
cin>>x;
int mx=0,id=0;
for(int i=1;i<x;i++)
{
if(__gcd(x,i)+i>mx)
{
mx=__gcd(x,i)+i;
id=i;
}
}
cout<<id<<"\n";
}

return 0;
}

B. Prefiquence

双指针,也很典

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;

int t,n,m;
string a,b;

int main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);

cin>>t;
while(t--)
{
cin>>n>>m>>a>>b;
int cn1=b.size(),cn2=a.size();
int i=0,j=0;
while(i<cn1&&j<cn2)
{
if(a[j]==b[i])
{
j++;
}
if(j==cn2)
{
break;
}
i++;
}
cout<<j<<"\n";
}
return 0;
}


C. Assembly via Remainders

比较简单的构造,保证每个$a[i-1]>x[i]$即可

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
#include<bits/stdc++.h>

using namespace std;

int t;
int n;
long long x[505],a[505];
long long pre;

int main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);

cin>>t;
while(t--)
{
cin>>n;
for(int i=2;i<=n;i++)
{
cin>>x[i];
}
a[1]=x[2]+1;
for(int i=2;i<=n;i++)
{
a[i]=a[i-1]+x[i];
while(a[i]<=x[i+1])
{
a[i]+=a[i-1];
}
}
for(int i=1;i<=n;i++)
{
cout<<a[i]<<" ";
}
cout<<"\n";
}

return 0;
}

D. Permutation Game

zhy女士讨论的思路甚至是对的。

大概是一个类似于线性dp的思路,以min(循环节长度, $k$ )为循环,这样循环大小不会超过 $2×10^5$

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
#include<bits/stdc++.h>

using namespace std;

long long t;
long long n,k,pb,ps;
long long a[200005],p[200005];
bool vis[200005];

long long score(long long x,long long k)
{
memset(vis,0,sizeof vis);

long long mx=0,sum=0;
while(!vis[x]&&k>0) //如果第一个循环节没有循环完并且还没有走k步
{
vis[x]=1;
mx=max(mx,sum+k*a[x]);
//sum+k*a[x]是走到这个点就不动了,与下一层循环里的再走一步再停下进行比较
sum+=a[x]; //走一步
k--; //还剩几步
x=p[x]; //下一个
}

return mx;
}

int main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);

cin>>t;
while(t--)
{
long long n,k,pb,ps;
cin>>n>>k>>pb>>ps;
for(long long i=1;i<=n;i++)
{
cin>>p[i];
}
for(long long i=1;i<=n;i++)
{
cin>>a[i];
}
long long A=score(pb,k),B=score(ps,k);
if(A>B) //比较
{
cout<<"Bodya\n";
}
else if(A<B)
{
cout<<"Sasha\n";
}
else
{
cout<<"Draw\n";
}
}

return 0;
}

E. Cells Arrangement

烂透构造题hmm,你知道我看到代码时有多崩溃吗=(

oh,原来读题读错了,人家让我求max,我搁这儿求min。。。(笑不出来

虽然有很多别的构造方式但是这里介绍一下官方题解的最短方法

注意到:

$n=2$ 时,随便放即可

n=2.png

$n=3$ 时,在上一个的基础上最外圈的上面填一个点,与剩下两个的距离不相等且不等于1

n=3.png

$n=4$ 同理

在一张 $n×n$ 的图里,我们一共可以得到 $1$ ~ $2(n-1)$ 的 $2(n-1)$ 个不同曼哈顿距离对角线上的点与右下产生所有偶数距离,与右下上一个点产生所有奇数距离

其实也没有很难qwq,代码不需要注释了吧…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include<bits/stdc++.h>

using namespace std;

int t,n;

int main()
{
cin>>t;
while(t--)
{
cin>>n;
for(int i=1;i<=n-2;i++)
{
cout<<i<<" "<<i<<"\n";
}
cout<<n-1<<" "<<n<<"\n"<<n<<" "<<n<<"\n";
}

return 0;
}

F.Equal XOR Segments

感觉这次的题都有点浓厚数学的成分

本题我借鉴了一下题解,发现 $k>3$ 是没有必要的,因为我们可以把任意连续三个异或和相同的序列合并成一个,即 $x⊕x⊕x=x$,这样就可以减少两个子序列

同时 $k=2$ 时, $x⊕x=0$

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
#include<bits/stdc++.h>

using namespace std;

long long t;
long long n,q;
long long a[200005];

map<long long,vector<long long> > id;

int main()
{
cin>>t;
while(t--)
{
cin>>n>>q;

for(auto &i:id)
{
i.second.clear();
}
id[0].push_back(0);

for(long long i=1;i<=n;i++)
{
cin>>a[i];
a[i]^=a[i-1]; //异或前缀和
id[a[i]].push_back(i);
}

while(q--)
{
long long l,r;
cin>>l>>r;

//如果头尾的异或和一样,那么说明这个序列可以被分成2个异或和相等的区间
if(a[r]==a[l-1])
{
cout<<"YES\n";
continue;
}

//我们将[l,r]分为[l,s],[s+1,t]和[t+1,r], 我们必须检查a[l-1]=a[t]和a[s]=a[r]
//同时满足s<t
long long t= *--lower_bound(id[a[l-1]].begin(),id[a[l-1]].end(),r);
long long s= *lower_bound(id[a[r]].begin(),id[a[r]].end(),l);

if(t>s)
{
cout<<"YES\n";
}
else
{
cout<<"NO\n";
}
}
cout<<"\n";
}
}

G&H.Division + LCP

一个简化版一个强化版,合一起做

这题似乎需要一种名字叫做 Z函数(扩展kmp) 的东西

【模板】扩展 KMP/exKMP(Z 函数)

  • Z函数可以解决的问题:

    线性求解字符串str以第i位开始的后缀与str的最长公共前缀(LCP),Z[i]表示第i位开始的后缀与str的最长公共前缀

然后这个题目说让我们求把这个字符串分成k段的LCP最大值,其中有一个子段一定是原字符串的前缀,所以最后得到的k个子段也肯定是原字符串的前缀

如果 $k$ 不大于 $\sqrt{n}$ ,那么只需要枚举不超过 $\sqrt{n}$ 次;否则用二分算答案即可。

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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
#include<bits/stdc++.h>

using namespace std;

int t,n;
int z[200005];
int ans[200005];

void Z(string s) //Z函数
{
int l=0,r=0;
for(int i=1;i<n;i++)
{
if(i<=r&&z[i-l]<r-i+1)
{
z[i]=z[i-l];
}
else
{
z[i]=max(0,r-i+1);
while(i+z[i]<n&&s[z[i]]==s[i+z[i]])
{
z[i]++;
}
}
if(i+z[i]-1>r)
{
l=i,r=i+z[i]-1;
}
}
}

int f(int len) //计算不小于len长度的最长前缀总数
{
int cnt=1;
for(int i=len;i<n;)
{
if(z[i]>=len)
{
cnt++,i+=len;
}
else
{
i++;
}
}
return cnt;
}

int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);

ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);

cin>>t;
while(t--)
{
memset(z,0,sizeof z);
memset(ans,0,sizeof ans);

int l,r;
string s;
cin>>n>>l>>r;
cin>>s;

Z(s);

// for(int i=0;i<n;i++)
// {
// cout<<z[i]<<" ";
// }
// cout<<"\n";

//如果k>sqrt(n),那么len<sqrt(n)
int sq=ceil(sqrt(n));
for(int i=sq;i>0;i--) //直接枚举即可
{
int cn=f(i);
ans[cn]=max(ans[cn],i);
}
for(int i=n-1;i>0;i--)
{
ans[i]=max(ans[i],ans[i+1]);
}

//如果k<sqrt(n)
for(int i=1;i<=sq;i++)
{
int L=0,R=n/i;
while(L<R)
{
int mid=(L+R+1)/2;
if(f(mid)>=i)
{
L=mid;
}
else
{
R=mid-1;
}
}
ans[i]=L;
}
for(int i=l;i<=r;i++)
{
cout<<ans[i]<<" ";
}
cout<<"\n";
}

return 0;
}


Finished.

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×