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
//逆元定义如下:
img
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;
}
posted @ 2020-07-21 21:35  cugbacm03  阅读(141)  评论(0)    收藏  举报