7.13 2020牛客暑期多校训练营(第二场)题解及补题
7.13 2020牛客暑期多校训练营(第二场)题解及补题
比赛过程
D题是签到题,后面做C和H,实在有点可惜,方法使用错误超时结束。C应该正确使用dfs序,H选取右小角的6*6区域暴力。
题解
B Boundary
题意
给定n个点,让你找最多有多少个点共圆并且该圆过原点。
解法
用求三角形外心的模板。遍历选取两个点和原点组成三角形,求三角形的外心并将点存起来,找出被覆盖最多次数的点即为圆心,是在圆上的点组成的多边形的边数,得到边数后求(int)sqrt(ans*2) +1就是答案。
代码
#include <algorithm>
#include <bitset>
#include <cctype>
#include <cerrno>
#include <clocale>
#include <cmath>
#include <complex>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <deque>
#include <exception>
#include <fstream>
#include <functional>
#include <limits>
#include <list>
#include <map>
#include <iomanip>
#include <ios>
#include <iosfwd>
#include <iostream>
#include <istream>
#include <ostream>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <stdexcept>
#include <streambuf>
#include <string>
#include <utility>
#include <vector>
#include <cwchar>
#include <cwctype>
using namespace std;
#define IO ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
const int inf = 0x3f3f3f3f;
typedef long long ll;
const int maxn = 2000500;
const ll mod = 1e9 + 7;
typedef pair<int,int> pii;
const double pi = acos(-1);
typedef long long ll;
typedef pair<double, double> pdd;
const int N = 2005;
struct point {
ll x, y;
}a[N];
vector<pdd> v;
//求三角形外心
void getc(const point& p1, const point& p2, const point& p3, pdd& center) {
ll x1 = p1.x;
ll x2 = p2.x;
ll x3 = p3.x;
ll y1 = p1.y;
ll y2 = p2.y;
ll y3 = p3.y;
ll t1=x1*x1+y1*y1;
ll t2=x2*x2+y2*y2;
ll t3=x3*x3+y3*y3;
double temp=x1*y2+x2*y3+x3*y1-x1*y3-x2*y1-x3*y2;
center.first = double(t2*y3+t1*y2+t3*y1-t2*y1-t3*y2-t1*y3)/temp;
center.second = double(t3*x2+t2*x1+t1*x3-t1*x2-t2*x3-t3*x1)/temp;
}
int main() {
IO;
int n;
cin>>n;
for(int i = 1; i <= n; ++ i)
cin>>a[i].x>>a[i].y;
point temp;
temp.x = 0, temp.y = 0;
for(int i = 1; i <= n; ++ i) {
for(int j = i + 1; j <= n; ++ j) {
pdd c;
if(a[i].x * a[j].y != a[i].y * a[j].x) {
getc(a[i], a[j], temp, c);
v.push_back(c);
}
}
}
sort(v.begin(),v.end());
int ans = 0, cnt = 0;
pdd tmp = v[0];
for(auto i: v) {
if(i == tmp) {
cnt++;
}
else {
ans = max(ans, cnt);
cnt = 1;
tmp = i;
}
}
ans = max(cnt, ans);
cout<<int(sqrt(ans*2))+1;
return 0;
}
C Cover the Tree
题意
题目给了一个无根树,要求找到最小数量的链,使得树上任意一个边至少被一个链包含,
输出链的最小数目ans,以及每一个链的第一个结点以及最后一个结点。
解法
要求找最小数目的链包含所有的边,贪心的思想肯定是找两端是叶子节点的链,那么统计一下叶子节点的数目cnt,如果是奇数个叶子结点,那么ans就是(cnt+1)/2,如果是偶数个叶子节点,那么ans就是cnt/2。
n<=2的时候答案显然,n>=3的时候,取任意一个非叶子节点作为根(这里我们选取了度数最多的点作为根),然后跑叶子节点的dfs序,那么假设cnt为偶数,我们构造的cnt/2条链就是l1-lcnt/2+1,l2-lcnt/2+2,……,lcnt/2-lcnt,如果cnt为奇数,那么只需要把最后一个剩下的叶子节点和根节点连接即可。
代码
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int n;
vector<int> g[200010];
int dfs[200020];//dfs序 数组
int len,tim;// len 是当前dfs序的长度, tim 是时间
int s[200020],e[200020];// 存的某点子树对应区间的起点 和终点, 标号i表示某点dfs序中的位置
int pos[200020];//某点i的 dfs序位置
int d[200020];
void DFS(int u,int fa)
{
int x=len+1;
if(d[u]==1){
s[++len]=++tim;// 起点是开始时间
dfs[len]=u;
pos[u]=len;
}
int sz=g[u].size();
for(int i=0;i<sz;i++)
{
if(g[u][i]!=fa)//不能dfs遍历父亲
{
DFS(g[u][i],u);
}
}
e[x]=tim;// 结束时间 即为以他为子树的终止区间
}
int main(){
scanf("%d",&n);
int m=n-1;
memset(d,0,sizeof(d));
int st=-1,stt=0;
for(int i=1;i<=m;i++){
int l,r;
scanf("%d%d",&l,&r);
g[l].push_back(r);
g[r].push_back(l);
d[l]++;
d[r]++;
if(d[l]>stt){
stt=d[l];
st=l;
}
if(d[r]>stt){
stt=d[r];
st=r;
}
}
DFS(st,0);
cout<<(len+1)/2<<endl;
if(len&1){
for(int i=1;i<=len/2;i++){
cout<<dfs[i]<<" "<<dfs[i+len/2]<<endl;
}
cout<<st<<" "<<dfs[len]<<endl;
}
else{
for(int i=1;i<=len/2;i++){
cout<<dfs[i]<<" "<<dfs[i+len/2]<<endl;
}
}
}
D Duration
题意
题目给了同一天中的两个时间点,格式为HH:MM:SS,然后要求输出两个时间点之间的秒数。
解法
签到题,只需要稍微计算一下即可。
代码
#include <bits/stdc++.h>
#define IO ios::sync_with_stdio(0), cin.tie(0)
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 5;
const int inf = ~0u >> 1;
typedef pair<int,int> P;
#define REP(i, a, n) for (int i = a; i < (n); ++i)
#define PER(i, a, n) for (int i = (n) - 1; i >= a; --i)
int main() {
IO;
int h1, h2, m1, m2, s1, s2;
scanf("%d:%d:%d", &h1, &m1, &s1);
scanf("%d:%d:%d", &h2, &m2, &s2);
int sum1 = h1 * 3600 + m1 * 60 + s1;
int sum2 = h2 * 3600 + m2 * 60 + s2;
int ans = abs(sum1 - sum2);
cout << ans << endl;
return 0;
}
F Fake Maxpooling
题意
题目给了一个nm的矩阵,矩阵中Ai,j=lcm(i,j),然后题目要输输出所有KK的子矩阵的最大值之和。
解法
首先跑一下nm的矩阵,然后当k<=6的时候用纯暴力即可,k>=7的时候,我们只需要贪心的寻找每一个KK的子矩阵的右下角的6*6的小矩阵求最大值即可。
代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define maxn 5005
ll n,m,k;
ll a[maxn][maxn];
ll gcd(ll b,ll c){return c==0?b:gcd(c,b%c);}
ll lcm(ll b,ll c){return b * c/ gcd(b, c);}
int main(){
scanf("%lld%lld%lld",&n,&m,&k);
for(ll i=1;i<=5000;i++){
for(ll j=i;j<=5000;j++){
a[i][j]=lcm(i,j);
a[j][i]=a[i][j];
}
}
ll ans=0;
if(k<=6){
for(ll i=0;i<=n-k;i++){
for(ll j=0;j<=m-k;j++){
ll tmp=0;
for(int w=1;w<=k;w++){
for(int e=1;e<=k;e++){
tmp=max(tmp,a[i+w][j+e]);
}
}
ans+=tmp;;
}
}
}else{
int l=-1,r=-1;
for(ll i=0;i<=n-k;i++){
for(ll j=0;j<=m-k;j++){
ll tmp=0;
for(int w=k-5;w<=k;w++){
for(int e=k-5;e<=k;e++){
tmp=max(tmp,a[i+w][j+e]);
}
}
ans+=tmp;;
}
}
}
cout<<ans<<endl;
}
J Just Shuffle
题意
对于一个排列A,给定一个置换规则P,在使用置换P K 次后得到新的排列B
解法
(置换群概念理解为博主Yoangh原创内容:链接)
首先给你一个序列,假如:
s = {1 2 3 4 5 6}
然后给你一个变换规则
t = {6 3 4 2 1 5}
就是每一次按照t规则变换下去
比如这样
第一次:6 3 4 2 1 5
第二次:5 4 2 3 6 1
第三次:1 2 3 4 5 6
发现经过几次会变换回去,在变换下去就是循环的了,这就是一个置换群。
我们可以这样表示一个置换群,比如按照上面变化规则
1->6->5->1 那么这些是一个轮换
2->3->4->2 这些是一个轮换
所以可以写为
t = { {1 6 5},{ 2 3 4 } }
解法:
A ^ K = B
且P等于A再置换一次
我们设Z为K的逆元,r为置换循环节,则 B ^ Z = A
//逆元定义如下:

Z * K % r == 0 ,(r为置换循环节)
令Z:Z * K % r == 1
求出Z,然后让B置换Z次即可得A。
代码
#include <algorithm>
#include <bitset>
#include <cctype>
#include <cerrno>
#include <clocale>
#include <cmath>
#include <complex>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <deque>
#include <exception>
#include <fstream>
#include <functional>
#include <limits>
#include <list>
#include <map>
#include <iomanip>
#include <ios>
#include <iosfwd>
#include <iostream>
#include <istream>
#include <ostream>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <stdexcept>
#include <streambuf>
#include <string>
#include <utility>
#include <vector>
#include <cwchar>
#include <cwctype>
using namespace std;
#define IO ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
const int inf = 0x3f3f3f3f;
typedef long long ll;
const int maxn = 2000500;
const ll mod = 1e9 + 7;
typedef pair<int,int> pii;
const double pi = acos(-1);
typedef long long ll;
const int N = 1e5 + 1;
int a[N], b[N], c[N];
vector <int > v;
void solve(int k)
{
int i, j;
int r = v.size(), inv;
for(i = 0; i < r; i ++)
if((ll)k * i % r == 1)
inv = i;
for(j = 0; j < r; j ++)
c[v[j]] = v[(j + inv) % r];
}
int main() {
IO;
int n, k;
cin>>n>>k;
for(int i = 1; i <= n; i ++)
cin>>b[i];
for(int i = 1; i <= n; i ++) {
if(!a[i]) {
v.clear();
int x = b[i];
while(!a[x]) {
a[x] = 1;
v.push_back(x);
x = b[x];
}
solve(k);
}
}
for(int i = 1; i <= n; i ++) {
if(i != n)
cout<<c[i]<<" ";
else
cout<<c[i]<<endl;
}
return 0;
}

浙公网安备 33010602011771号