天天看点

2020牛客暑期多校训练营(第三场)题解

A :Clam and Fish

简要题意 •

小月有 n 单位的时间都在钓鱼,每个单位时间有四种状态,

。蛤蜊 鱼

0:0 // 0

1:0 //1

2:1//0

3: 1//1

小月事先知道这 n 个时间点的状态。每个时间点有四种可能 的动作:

  1. 若该时间点有鱼,则可以直接钓鱼。
  2. 若该时间点有蛤蜊,则可以用该蛤蛎制作一个鱼饵。
  3. 若该时间点身上有至少一个鱼饵,则可以用一个鱼饵钓一条鱼,钓完后就少 了一个鱼饵。
  4. 什么事都不做。

    请问小月最多可以钓多少条鱼。

    输入

    2

    4

    0103

    1

    1

    输出

    2

    备注:

    One possible scenario you can catch two fishes is as follow:

    stage 1: Do nothing.

    stage 2: Make a pack of fish bait.

    stage 3: Catch a fish by a pack of fish bait.

    stage 4: Catch the fish that appears in the stage.

状态2,3 有鱼直接钓鱼,状态1,0顺序配合钓鱼,状态1,1顺序配合钓鱼

#include<bits/stdc++.h>
using namespace std;
#define ll long long
string s;
int yi;
int main()
{
 ios::sync_with_stdio(false);
 int t,n,m,yu=0;
 cin>>t;
 while(t--)
 {
 	yu=0;yi=0;
 	cin>>n;
 	cin>>s;
 	for(int i=0;i<n;i++)
 	{
 	  if(s[i]=='2'||s[i]=='3') yu++;
	  else if(s[i]=='1') yi++;
	  else	
	  {
	  	if(yi) 
	  	{
	  	 yi--;yu++;	
		}
	  }
    }
    yu+=yi/2;
	cout<<yu<<endl;
 }
 return 0;	
} 
           

B:Classical String Problem

简要题意:

有一个字符串,有两种操作:

  1. 询问第 x 个字符。
  2. 把最左边的 x 个字符搬到最右边或把最右边 x 个字符搬到最左边。

    官方题解:

    想像成字符串是头尾接在一起的 ,只需维护一个指针指向当前第一个字符 的位置,就能以 O(1) 的时间复杂度进行每个操作。

    • 以 Sample 为例 •

    初始时指针在 n 的前面 |nowcoder •

    操作 1 询问指针右边第 1 个字符,答案是 n •

    操作 2 把 4 个字符搬到右边,相当于把指针向右移 4 个字符 nowc|oder •

    操作 3 询问指针右边第 6 个字符,答案是 o •

    操作 4 把右边 3 个字符搬到右边,相当于把指针向左移 3 个字符 n|owcoder •

输入

nowcoder

6

A 1

M 4

A 6

M -3

M 1

A 1

输出

n

o

w

#include<bits/stdc++.h>
using namespace std;
#define ll long long
int main()
{
 ios::sync_with_stdio(false);
 int n,tmp,tip=0,len;
 string s;
 char c;
 cin>>s>>n;
 len=s.length();
 while(n--)
 {
 	cin>>c>>tmp;
 	if(c=='A') cout<<s[(tip+tmp-1)%len]<<endl;
 	else 
	{
      if(tmp>0) tip=(tip+tmp)%len;
      else tip=(tip+tmp+len)%len;      
	}
 }
 return 0;	
} 
           

C:Operation Love

图为某机器人右手手印的形状,左手为右手的对称图形,现在给你一个机器人 手印(可能经过平移及旋转,大小不变),请判断是左手还右手(保证一定是其中一只手) 。

2020牛客暑期多校训练营(第三场)题解

找出边长为6和9的两条边,判断顺逆,注意精度

#include<bits/stdc++.h>
using namespace std;
#define ll long long
double x[25],y[25];
const double eps=0.1; //偏差值,有时用1e-10 
int sgn(double x)//判断x是否等于0 
{
 if(fabs(x)<eps) return 0;	
 return x<0? -1:1; 
} 
struct point 
{
	double x,y;
	point(){}
	point(double x,double y):x(x),y(y){}
	point operator-(point B) {return point(x-B.x,y-B.y);} //向量减法
};
double dis(point a,point b)
{
	return (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y);
}
double cross(point a,point b)
{
	return a.x*b.y-a.y*b.x;
}
point a[25];
int main()
{
 ios::sync_with_stdio(false);
 int t;
 cin>>t;
 while(t--)
 {
 	for(int i=0;i<20;i++)
 	 cin>>a[i].x>>a[i].y;
 	int bo;
 	for(int i=0;i<20;i++)
 	{
 	 if(sgn(dis(a[i],a[(i+19)%20])-36)==0&&sgn(dis(a[i],a[(i+1)%20])-81)==0)  
 	 {
 	  bo=sgn(cross(a[(i+19)%20]-a[i],a[(i+1)%20]-a[i]));
	  break;	
	 }
 	 if(sgn(dis(a[i],a[(i+19)%20])-81)==0&&sgn(dis(a[i],a[(i+1)%20])-36)==0) 
 	 {
 	  bo=sgn(cross(a[(i+1)%20]-a[i],a[(i+19)%20]-a[i]));
	  break;	
	 } 	 
	}
 	if(bo==-1) cout<<"right"<<endl;
 	else cout<<"left"<<endl;
 }
 return 0;	
} 
           

D:Points Construction Problem

在无限大的格子盘面中,原本所有格子都是白色的,请把其中 n 个格 子涂黑,使得满足格子 x 和格子y 是上下左右相邻且 x , y 异色的 (x,y) 组数恰好为k 个((x,y)和(y,x) 视为相同)。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
int main() 
{
    ios::sync_with_stdio(false);
	int t,n,m,x,y,sum,tmp,res; 
    cin>>t;
    while(t--)
    {
        cin>>n>>m;
        if(m%2||m>4*n||m*m<16*n)//m为偶数,最大值4n,最小值m^2>=16n 
         cout<<"No"<<endl;
        else
        {
        	cout<<"Yes"<<endl;
        	int sum=2*n+2;//当n为一排过去时的边数
        	if(m>sum) //两种情况,一些在一排里,一些落单 
        	{
        		tmp=(m-sum)/2;// 
        		for(int i=1;i<=n-tmp;i++) //一排过去,每次边+2 
        		 cout<<1<<" "<<i<<endl;
        		for(int i=3;i<=3+tmp-1;i++) //斜线,每次边+4 
        		 cout<<i<<" "<<i<<endl;
			}
			else//L型,最佳情况,abs(x-y)<=1
			{
				//m=2(x+y),极值在abs(x-y)<=1处取到,所以x=m/4 
				x=m/4;y=m/2-x; // 
				for(int i=1;i<=x;i++)
				 cout<<i<<" "<<1<<endl;
				for(int i=2;i<=y;i++)
				 cout<<1<<" "<<i<<endl;
				//L型,往里填方格,边不变 
				res=n-(x+y-1);//已填x+y-1个 
				for(int i=2;i<=x&&res;i++)
				 for(int j=2;j<=y&&res;j++,res--)
				  cout<<i<<" "<<j<<endl;
			}
		}
    }
    return 0;
}
           

E:Two Matchings

详解

dp[i]=min(dp[i-4]+dp[i]-dp[i-3],dp[i-6]+dp[i]-dp[i-5]);

#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll dp[200100]; 
int main()
{
 ios::sync_with_stdio(false);
 int t,n;
 cin>>t;
 while(t--)
 {
 	cin>>n;
 	for(int i=1;i<=n;i++)
 	 cin>>dp[i];
 	sort(dp+1,dp+n+1);
 	dp[4]-=dp[1];
 	dp[6]-=dp[1];
 	dp[8]=dp[8]-dp[5]+dp[4];
 	for(int i=10;i<=n;i+=2)
 	 dp[i]=min(dp[i-4]+dp[i]-dp[i-3],dp[i-6]+dp[i]-dp[i-5]);
 	cout<<2*dp[n]<<endl;
 }
 return 0;	
} 
           

F: Fraction Construction Problem

详解

#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll gcd(ll a,ll b) 
{
    return b==0? a:gcd(b,a%b);
}
void exgcd(ll a,ll b,ll &x,ll &y)
{
    if(!b)
    {
        x=1;y=0;return;
    }
    exgcd(b,a%b,y,x);
    y-=a/b*x;
}
int pri[2000100],cnt=0;
bool st[2000100];
int m[2000100];//数字i的最小因子(除了1)
void prime()
{
 m[0]=m[1]=1;
 for(int i=2;i<=2000000;i++)
  if(m[i]==0)
  {
   for(int j=i;j<=2000000;j+=i)
    if(m[j]==0)
    m[j]=i;
  }
}
int main()
{
 ios::sync_with_stdio(false);
 int t,a,b,g;
 cin>>t;
 prime();
 while(t--)
 {
   cin>>a>>b;
   g=gcd(a,b); 
   if(g>1)
    cout<<a/g+b/g<<" "<<b/g<<" "<<1<<" "<<1<<endl;
   else
   {
   	//找b的两个互质因子 
    ll d=1,f=b,k=m[b];
    while(k!=1&&f%k==0) d*=k,f/=k;
    if(d==b&&f==1) cout<<-1<<" "<<-1<<" "<<-1<<" "<<-1<<endl;
    else
    {
     ll e,c;
        exgcd(d,f,e,c);//ed+cf=1 
        //-ed+cf=1
        e*=-1;
        //ax+by=gcd(a,b)的其他解为x=x0+k*b; y=y0-k*a
        while(e<=0||c<=0) c+=d,e+=f;
        e*=a;c*=a;
        cout<<c<<" "<<d<<" "<<e<<" "<<f<<endl;
    }
   }
 }
    return 0;
}
           

G:Operating on a Graph

官方题解:

给一个 n 个点的 Graph,第 i 个点一刚开始是第 I 种颜色,接着有 k 次 操作,第 i 次操作有个参数 oi 代表颜色 oi 会侵略所有和自己相邻的颜色, 于是所有和 oi 相邻的颜色全都变成 oi (若已没有颜色oi 已被侵略,则该次操作无效),求最终每个点的颜色。

  1. 对于每个颜色都维护一个点的链表,储存该颜色中尚未把所有相邻点变为自己颜色的点。
  2. 用并查集纪录每个点属于哪个颜色。
  3. 每次操作就会把颜色 oi 的链表中所有相邻的点所属的颜色都变为 oi , 就直接把对应的链表合并以及在并查集上做对应的操作、
#include<bits/stdc++.h>
using namespace std;
#define ll long long
list<int>G[800100];
int t,n,m,u,v,q,num,fa[800100],fx,fy;
int find(int x)     //找到并更新最终结点
{
    if(x!=fa[x])
     x=fa[x]=find(fa[x]);
    return x;
}
void merge(int x,int y) // 
{
    fx=find(x);
    fy=find(y);
    if(fx==fy) return; //本来就在一组,不用修改 
    fa[fy]=fx;  
    G[fx].splice(G[fx].end(),G[fy]);// 合并 
    G[fy].clear();  //删去
}
int main() 
{
    cin>>t;
    while(t--)
    {
        cin>>n>>m;
        for(int i=0;i<n;i++) //一开始指向自己+初始化 
        {
            fa[i]=i;
            G[i].clear();
        }
        for (int i=0;i<m;i++)//存入边 
        {
            scanf("%d%d",&u,&v);
            G[u].push_back(v);
            G[v].push_back(u);
        }
        cin>>q;
        while(q--)
        {
            cin>>num;
            if(find(num)!=num)
                continue;
            //把相连的点归到自己一组 
            vector<int>v;
            for(auto &j: G[num])  //相连的点 
            {
                if(find(j)!=num)  //需要修改的点 
                 v.push_back(j);
            }
            G[num].clear(); //清空 
            for(auto &j:v)    //修改 
            {
                merge(num,j);
            }
        }
        for (int i=0;i<n;i++)
            cout<<find(i)<<" ";
        cout<<endl;
    }
    return 0;
}
           

L:Problem L is the Only Lovely Problem

判断一个字符串是否为 lovely 开头,不分大小写。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
int main()
{
 ios::sync_with_stdio(false);
 string s;
 cin>>s;
 for(int i=0;i<6;i++)
  if(s[i]>='A'&&s[i]<='Z')
   s[i]=s[i]-'A'+'a';
 if(s.length()>=6&&s.substr(0,6)=="lovely") cout<<"lovely"<<endl;
 else cout<<"ugly"<<endl;
 return 0;	
} 
           

继续阅读