7.18 2020牛客暑期多校训练营(第三场)题解及补题
7.18 2020牛客暑期多校训练营(第三场)题解及补题
比赛过程
D题,B题,A题,C题。
D题签到。AB同时开的,B其实可以看成环思路,只不过一开始str+=str,太费时间了,改了这立马过;A题一开始愣是没看懂题意,其实真正需要分析的是情况1;C题最后分析的是长度为9的边和长度为8的边,其实想想就是题面让你看的恶心,就是(用我们的解法)两个带60°角的三角形是正的还是反的。
题解
A Clam and Fish
题意
有一个捕鱼游戏,共有n个池塘,编号1-n,每个池塘有四种状态,分别为0,1,2,3.
0代表:没有鱼也没有蛤
1代表:没有鱼,有一条蛤
2代表:有一条鱼,没有蛤
3代表:有一条鱼和一条蛤
对于每一个池塘,我们有四种选择:
1.如果池塘里有蛤,可以用这个蛤制作一包鱼饵,所拥有的鱼饵包装数将增加一。在此操作之后,您可以使用此包鱼饵捕获鱼。
2.如果池塘里只有一条鱼,则无需任何鱼饵就可以抓到这条鱼。在此操作之后,拥有的鱼饵包数不会改变。
3.如果有至少一包鱼饵。即使在此阶段没有鱼,也可以使用一包鱼饵捕获一条鱼。在此操作之后,拥有的鱼饵包数将减少一。
4.什么也不做
解法
本题为贪心,我们的策略为,如果池塘为1,那么制作鱼饵;如果池塘为0并且已经有了鱼饵,使用鱼饵捕一条鱼;如果池塘为2或者3,
补一条鱼,由于最后鱼饵可能会有剩余的,所以最后答案要加上cnt/2,也就是1号池塘有一半是需要制作鱼饵,另一半是来捕鱼。
代码
#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 t;
cin >> t;
while (t--) {
int n;
cin >> n;
string s;
cin >> s;
int cnt = 0;
int ans = 0;
REP(i, 0, n) {
if (s[i] == '1') {
cnt++;
}
else if (s[i] == '0' && cnt > 0) {
ans++;
cnt--;
}
else if (s[i] == '3' || s[i] == '2') {
ans++;
}
}
cout << ans + cnt / 2 << endl;
}
return 0;
}
B Classical String Problem
题意
给一个由小写字母组成的字符串,以及Q个操作,操作有两种类型,操作M表示修改字符串:给定整数x,需要根据x的值修改S。
如果x为正,则将S中最左边的x个字母移到S的右边;否则,移动最右边的|x|长度到S左侧;操作A表示询问字符:给定正整数x。
输出当前字符串S中的第x个字母是什么。
解法
该题题意比较好理解,但是要注意超时的问题,由于M操作比较费时,我们不可以模拟每一个操作,我们需要记录每一次操作S
字符的移动距离,当询问字符时,只需要O(1)输出对应位置的字符即可,剩下的就是取模的细节了。
代码
#include<bits/stdc++.h>
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);
int main() {
IO;
string str;
cin>>str;
int T;
cin>>T;
int cnt=0;
int len=str.length();
while(T--) {
char ch;
int x;
cin>>ch>>x;
if(ch=='M') {
if(x>0) {
cnt+=x;
cnt%=(len);
}
else {
cnt+=x;
if(cnt<0) {
cnt+=(len);
}
}
}
else {
cout<<str[(cnt+x-1)%len]<<endl;
}
}
return 0;
}
C Operation Love
题意
t组测试,每次测试按顺时针或逆时针给20个点,是一个手的形状,问是左手还是右手。
解法
这里放的是训练时的比较“野”的一种做法:
题图如上,因为左手和右手是镜面对称的,所以不同点是很容易找到的;另外,给定的点是顺时针或者逆时针给出的,所以要是想根据点的输入判断左手还是右手是不可能的。可以选择凸包什么的,但是当时看到题面里面提到,手的形状是不会放大或者缩小的,但是可以旋转和平移,平移的话,所有的点加或者是减都是一样的形状。
所以选定了(1,0),(10,0),(10,8)相对应的三个点,将(10,0)的坐标置为0,判断其他两个点的象限即可。
方法比较“野”,但是不失为一种方法。
代码
#include<bits/stdc++.h>
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);
struct node
{
double x,y;
}a[25],b,c,d;
double cal(node a,node b) {
return sqrt((a.x-b.x)*((a.x-b.x))+(a.y-b.y)*(a.y-b.y));
}
int main() {
IO;
int T;
cin>>T;
while(T--) {
cin>>a[1].x>>a[1].y;
for(int i=2;i<=20;i++) {
cin>>a[i].x>>a[i].y;
}
bool flag=true;
a[0].x=a[20].x;
a[0].y=a[20].y;
a[21].x=a[1].x;
a[21].y=a[1].y;
for(int i=1;i<=20;i++) {
if(cal(a[i],a[i-1])>9.00-1e-1&&cal(a[i],a[i-1])<9.00+1e-1 && cal(a[i],a[i+1])>8.00-1e-1&&cal(a[i],a[i+1])<8.00+1e-1) {
c.x=a[i].x;
c.y=a[i].y;
b.x=a[i+1].x-c.x;
b.y=a[i+1].y-c.y;
d.x=a[i-1].x-c.x;
d.y=a[i-1].y-c.y;
flag=false;
break;
}
else if(cal(a[i],a[i+1])>9.00-1e-1&&cal(a[i],a[i+1])<9.00+1e-1 && cal(a[i],a[i-1])>8.00-1e-1&&cal(a[i],a[i-1])<8.00+1e-1) {
c.x=a[i].x;
c.y=a[i].y;
b.x=a[i-1].x-c.x;
b.y=a[i-1].y-c.y;
d.x=a[i+1].x-c.x;
d.y=a[i+1].y-c.y;
flag=false;
break;
}
}
if(b.y==0) {
if(b.x>0&&d.y>0) cout<<"right\n";
else if(b.x>0&&d.y>0) cout<<"left\n";
else if(b.x<0&&d.y>0) cout<<"left\n";
else cout<<"right\n";
}
else if(b.x==0) {
if(b.y>0&&d.x>0) cout<<"left\n";
else if(b.y<0&&d.x>0) cout<<"right\n";
else if(b.y>0&&d.x<0) cout<<"right\n";
else cout<<"left\n";
}
else if(b.x>0&&b.y>0) {
if(d.x>0) cout<<"left\n";
else cout<<"right\n";
}
else if(b.x<0&&b.y>0) {
if(d.x>0) cout<<"left\n";
else cout<<"right\n";
}
else if(b.x<0&&b.y<0) {
if(d.x<0) cout<<"left\n";
else cout<<"right\n";
}
else {
if(d.x<0) cout<<"left\n";
else cout<<"right\n";
}
}
return 0;
}
D Points Construction Problem
题意
在2D平面内,每个格点(整数点)有一个白点,可以将其中一些点涂黑。问能否将n个白点涂黑,使得有m对相邻的白点和黑点(指哈夫曼距离为1)
解法
借鉴一位大佬的比赛时的思路,感觉好理解
①若每个黑点都是不相邻的,则可以发现涂黑n个的黑白对数上限为\(4*n\)。
②如果每个黑点相邻,并排成一行,则此时黑白对数为\(2*n+2\)。
③可以发现黑白点对数一定是偶数,在②的基础上,将链上的黑点逐个拿出并放置在互不相邻处,则可以实现\(2∗n+2\)至$ 4∗n$所有偶数m的构造。
④如果m比\(2∗n+2\)还要小,可以在②的基础上,将这条黑点链逐步卷曲来减少黑白点对数,每次减2
代码
#include <bits/stdc++.h>
using namespace std;
int vis[100][100];
int cal(int u,int v){//周围黑点的个数
return vis[u-1][v]+vis[u+1][v]+vis[u][v+1]+vis[u][v-1];
}
int main () {
int t;
scanf("%d",&t);
while(t--){
int n,m;
scanf("%d%d",&n,&m);
if(n*4<m || m%2==1){
puts("No");
continue;
}
int srt=sqrt(n);
int rest=n-srt*srt;
int down=srt*4;
if(rest>0){
down+=2;
}
if(rest>srt){
down+=2;
}
if(m<down){
puts("No");
continue;
}
puts("Yes");
if((n*4-m)/2<n){//单链
int num2=(n*4-m)/2;
int num4=n-num2;
for(int i=1;i<=num2+1;i++){
printf("%d %d\n",i,1);
}
num4--;
for(int i=1;i<=num4;i++){
printf("%d %d\n",-i,-i);
}
}
else{//进行卷
memset(vis,0,sizeof vis);
int num0=(2*n+2-m)/2;
int cnt=0;
vis[50][50]=1;
cnt++;
printf("50 50\n");
int x=50,y=50;
int turn=0;//0右,1上,2左,3下
for(int step=1;step<=10;step++){
for(int sstep=1;sstep<=2;sstep++){//同一步长走两次
if(turn==0){
for(int i=1;i<=step;i++){
x++;
vis[x][y]=1;
cnt++;
printf("%d %d\n",x,y);
if(cal(x,y)==2){
num0--;
}
if(num0==0 ||cnt==n){
break;
}
}
if(num0==0||cnt==n){
break;
}
turn=(turn+1)%4;
}
else if(turn==1){
for(int i=1;i<=step;i++){
y++;
vis[x][y]=1;
cnt++;
printf("%d %d\n",x,y);
if(cal(x,y)==2){
num0--;
}
if(num0==0 ||cnt==n){
break;
}
}
if(num0==0||cnt==n){
break;
}
turn=(turn+1)%4;
}
else if(turn==2){
for(int i=1;i<=step;i++){
x--;
vis[x][y]=1;
cnt++;
printf("%d %d\n",x,y);
if(cal(x,y)==2){
num0--;
}
if(num0==0 ||cnt==n){
break;
}
}
if(num0==0||cnt==n){
break;
}
turn=(turn+1)%4;
}
else if(turn==3){
for(int i=1;i<=step;i++){
y--;
vis[x][y]=1;
cnt++;
printf("%d %d\n",x,y);
if(cal(x,y)==2){
num0--;
}
if(num0==0 ||cnt==n){
break;
}
}
if(num0==0||cnt==n){
break;
}
turn=(turn+1)%4;
}
}
if(num0==0||cnt==n){
break;
}
}
if(cnt!=n){
if(turn==0){//右转下
while(cnt<n){
y--;
printf("%d %d\n",x,y);
cnt++;
}
}
else if(turn==1){//上转右
while(cnt<n){
x++;
printf("%d %d\n",x,y);
cnt++;
}
}
else if(turn==2){//左转上
while(cnt<n){
y++;
printf("%d %d\n",x,y);
cnt++;
}
}
else if(turn==3){//下转左
while(cnt<n){
x--;
printf("%d %d\n",x,y);
cnt++;
}
}
}
}
}
}
E Two Matchings
题意
给你一个数列,从a1−>an,你需要找到两个匹配序列p,q其中\(pi≠qi\),
使得(∑(n i=1)abs(ai−api))/2+(∑(n i=1)abs(ai−aqi))/2最小,问这个最小值是多少。n保证为偶数,sum(n)<=2e5
匹配序列的定义为Pi!=i并且对于所有的Ppi=i
解法
第一个序列P是可以确定的最小值,也就是对a数组排序后将每相邻的两个相互连接,序列Q使用DP维护最小值,至于如何DP,
我们可以观察题目得出所有长度大于8的序列,都可以分解为长度为4或者长度为6的序列,剩下的就是贪心计算最小边权之和。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int inf=0x3f3f3f3f;
const int N=2e5+100;
int a[N];
ll dp[N];
int main(){
int t;
cin>>t;
while(t--){
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",a+i);
sort(a+1,a+1+n);
dp[0]=0;
dp[2]=inf;
dp[4]=a[4]-a[1];
for(int i=6;i<=n;i+=2)
dp[i]=min(dp[i-4]+a[i]-a[i-3],dp[i-6]+a[i]-a[i-5]);
printf("%lld\n",2*dp[n]);
}
return 0;
}
F Fraction Construction Problem
题意
给两个正整数a和b,要求找到四个正整数c,d,e,f,满足\(\frac{a}{b}=\frac{c}{d}-\frac{e}{f}\)。
解法
此题看到数学公式想到去构造,想怎么构造出来这个式子
①如果分子分母存在公共因子,b不是质数,构造\(\frac{a}{b}=\frac{a+x}{b}-\frac{x}{b}\),然后我们对分子分母同除以a,b的一个公因子,我们设为g,那么就会有\(\frac{a}{b}=\frac{\frac{a+x}{g}}{\frac{b}{g}}-\frac{\frac{x}{g}}{\frac{b}{g}}\),由于x是任意的,x=g,所以\(\frac{a}{b}=\frac{\frac{a}{g}+1}{\frac{b}{g}}-\frac{1}{\frac{b}{g}}\)。
②如果分子分母最简化了,分母的质因子不超过一个,比如8,16,那么无解。
③分子分母最简化,分母的质因子超过一个,有\(df=b\),\(gcd(d,f)=1\)。那么只要找到d,f,然后令\(cf-ed=a\),也就是\(fx-dy=gcd(f,d)*a\)。熟悉的配料,扩展欧几里得。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2e6+10;
int min_prim[maxn],prim[maxn];
int vis[maxn];
void init()
{
int m=sqrt(maxn);
for (int i=2; i<m; i++){
if (!vis[i]){
min_prim[i]=i;
for (int j=i*i; j<maxn; j+=i){
vis[j]=1;
if (!min_prim[j]) min_prim[j]=i;
}
}
}
for (int i=1; i<maxn; i++){
if (!vis[i]) continue;
prim[i]++;
int x=i;
while (x%min_prim[i]==0) x/=min_prim[i];
if (x!=1) prim[i]++;
}
}
ll exgcd(ll a,ll b,ll &x,ll &y)
{
if (!b) {
x=1; y=0;
return a;
}
ll d=exgcd(b,a%b,y,x);
y-=a/b*x;
return d;
}
int main()
{
int t;
init();
scanf ("%d",&t);
while (t--){
int a,b;
scanf ("%d%d",&a,&b);
int g=__gcd(a,b);
if (g!=1) {
printf("%d %d %d %d\n",a/g+1,b/g,1,b/g);
continue;
}
if (prim[b]<=1) {printf("-1 -1 -1 -1\n"); continue;}
ll d=1,f=b;
while (f%min_prim[b]==0) {
f/=min_prim[b];
d*=min_prim[b];
}
ll c,e;
exgcd(f,d,c,e);
e=-e;
while (e<0 || c<0) e+=f,c+=d;
printf ("%lld %lld %lld %lld\n",1LL*c*a,d,1LL*e*a,f);
}
return 0;
}
G Operating on a Graph
题意
给你一个由\(\,n\,\)个点\(\,m\,\)条边组成的无向图,起初每个顶点\(\,v_i\,\)属于\(\,i\,\)团。有\(\,q\,\)次查询,每次查询给你一个\(\,o_i\,\),将与\(\,o_i\,\)团相邻的团合并到\(\,o_i\,\)团中。
解法
利用并查集记录每个顶点属于那个团,再用vector存每个团的相邻的点,注意合并团的时候,需要把被合并的团的相邻点也存到当前团的相邻点中。具体操作看代码注释。
代码
#include <bits/stdc++.h>
#define IO ios::sync_with_stdio(0), cin.tie(0)
using namespace std;
typedef long long ll;
const int maxn = 8e5 + 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 f[maxn];
int find(int x) { return f[x] == x ? x : f[x] = find(f[x]); }
vector<int> e[maxn];
int main() {
int t;
scanf("%d", &t);
while (t--) {
int n, m;
scanf("%d%d", &n, &m);
REP(i, 0, n) {
f[i] = i;
e[i].clear();
}
REP(i, 0, m) {
int u, v;
scanf("%d%d", &u, &v);
e[u].push_back(v);
e[v].push_back(u);
}
int q;
scanf("%d", &q);
while (q--) {
int o;
scanf("%d", &o);
if (find(o) != o) { //没有点属于o团,就没有任何操作可做
continue;
}
vector<int> temp = e[o];
e[o].clear();
for (auto it : temp) { //遍历此时与o团相邻的点
int num = find(it); //此时相邻点所属的团
if (num == o)
continue; //如果相邻点已经在o团内,就不用做任何操作
f[num] = o; //否则就把这个相邻点合并到o团中
if(e[o].size() < e[num].size())
swap(e[o], e[num]);
e[o].insert(e[o].end(), e[num].begin(), e[num].end());//将新合并到o团中的这个点的相邻点也记录到o团的相邻点中
}
//REP(i, 0, n) { printf("%d ", find(i)); }
}
REP(i, 0, n) { printf("%d ", find(i)); }
printf("\n");
}
return 0;
}
L Problem L is the Only Lovely Problem
题意
签到题。给一个字符串,要求判断是不是以“lovely”为开头,如果是,输出lovely,否则输出ugly。
解法
使用string的find函数即可,注意大小写。
代码
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const ll maxn=2e6+5;
const ll mod =1000000007;
int main(){
string s;
while(cin>>s){
int len=s.length();
for(int i=0;i<len;i++){
if(isupper(s[i])){
s[i]=tolower(s[i]);
}
}
int p;
if((p=s.find("lovely"))==0){
cout<<"lovely"<<endl;
}else{
cout<<"ugly"<<endl;
}
}
}

浙公网安备 33010602011771号