「周练」Codeforces Round #530 (Div. 2)

Kara ·
更新时间:2024-09-20
· 666 次阅读

目录一. Codeforces Round #530 (Div. 2)A. Snowball (模拟)B. Squares and Segments (数学)C. Postcard (贪心)D. Sum in the tree (dfs+贪心)E. Nice table (找规律+模拟)F. Cookies (待补)二. 二分练习A. Aggressive cows (最大化最小值)B. String Game (二分下标)C. Delivering Carcinogen (二分+计算几何) 一. Codeforces Round #530 (Div. 2)

比赛网址链接:Codeforces Round #530 (Div. 2)

A. Snowball (模拟)

题意

  一个重量为 www 的雪球从高度 hhh 滚下,每下降一单位,重量的增加量为当前高度,过程有两个石头,雪球经过石头重量的减少量为石头的重量,问雪球到达地面时的重量

思路

  按照题意模拟即可,注意过程重量不会减成负值

代码

#include #include #include #include #include #include #define ft first #define sd second #define nul string::npos #define ms(x,y) memset(x,y,sizeof(x)) using namespace std; typedef long long ll; typedef pair P; const int maxn=2e5+7; const int inf=0x3f3f3f3f; int w,h,u1,u2,d1,d2; int main() { cin>>w>>h>>u1>>d1>>u2>>d2; int pos=h,nw=w; while(pos){ nw+=pos; if(pos==d1){ nw-=u1; nw=max(nw,0); } else if(pos==d2){ nw-=u2; nw=max(nw,0); } pos--; } cout<<nw<<'\n'; return 0; } B. Squares and Segments (数学)

题意

  给一个数 nnn,找出两个数,使得它们的乘积不小于 nnn,要求最小化这两个数的和

思路

  开始读错题了,以为要用这 nnn 个小正方形组成一个矩形,后来发现不是,那就好办了,最小化两个数的和,肯定这两个数要比较接近,那就不断增大两个数,知道它们的乘积不小于 nnn 即可

代码

#include #include #include #include #include #include #define ft first #define sd second #define nul string::npos #define ms(x,y) memset(x,y,sizeof(x)) using namespace std; typedef long long ll; typedef pair P; const int maxn=2e5+7; const ll inf=1e15; ll n; int main() { cin>>n; ll sum=n+1; ll l=1,r=1; while(l*rr)r++; else l++; } cout<<l+r<<'\n'; return 0; } C. Postcard (贪心)

题意

  一个字符串里有小写字母、∗*∗ 符号、??? 符号,这两种符号都可以将它们前方的字母删除、保留,不同的是 ∗*∗ 符号还可以无穷多次复制其前面的字母,问这个串能否构造出一个长度为 kkk 的串

思路

  首先考虑不存在的情况,那就只有两种情况,一是字符串里没有 ∗*∗ 符号,但 kkk 太大,二是 kkk 太小,能删的全删也构造不出长度为 kkk 的。
  之后就分类讨论就好,根据原串我们统计的信息,如果 kkk 比较大,那就确定好要复制的字母和个数,剩下的字母都保留即可,如果 kkk 比较小,那就确定要删除的字母个数,先删够,剩下的字母都保留即可

代码

#include #include #include #include #include #include #define ft first #define sd second #define nul string::npos #define ms(x,y) memset(x,y,sizeof(x)) using namespace std; typedef long long ll; typedef pair P; const int maxn=2e5+7; const ll inf=1e15; string s,ans; int f1,f2,id=-1; int n,sum[5];//sum[0]有意义的?和*个数、sum[1]无意义的*和?个数、sum[2]字母个数 int main() { cin>>s>>n; int len=s.length(); for(int i=0;i=0&&(s[i-1]>='a'&&s[i-1]=0&&(s[i-1]>='a'&&s[i-1]sum[2]&&!f2)||(n=sum[3]){//太多的情况 ans=""; int ln=n-sum[2]; for(int i=0;i='a'&&s[i]<='z')ans+=s[i]; } for(int i=1;i<=ln;i++)ans+=s[id-1]; for(int i=id+1;i='a'&&s[i]<='z')ans+=s[i]; } cout<<ans<<'\n';return 0; } int sc=sum[2]-n; for(int i=0;i='a'&&s[i]<='z'){ if(i+1<len&&(s[i+1]=='*'||s[i+1]=='?')){ if(sc){ sc--; } else ans+=s[i]; i++; } else { ans+=s[i]; } } else continue; } cout<<ans<<'\n'; return 0; } D. Sum in the tree (dfs+贪心)

题意

  现在有一棵树,根为 111,iii 号结点的权值为 aia_iai​,sis_isi​ 为 iii 号结点到根路径上的总权值,现在只知道深度为奇数的结点的 sis_isi​ 值,让你推出 所有结点的 aia_iai​ 值,要求它们的 aia_iai​ 总和最小

思路

  我们会发现一个规律就是,给越靠近根的结点赋值越大,它们的子孙赋值就越小,那么 aia_iai​ 总和就会越小,所以我们要想办法把靠近根的结点赋上尽可能大的值,离根越远的赋尽可能小的值
  对于 −1-1−1 的情况肯定就是对于某一个结点,存在一个子孙结点的 sis_isi​ 值比其本身的要小,因为 aia_iai​ 非负,所以从根到叶子的路径 sis_isi​ 值肯定不会递减。然后我们尝试还原深度为偶数的每个点的 sis_isi​ 值。
  根据前面的分析,很容易得出 su=min(sv)(v是u的儿子)s_u=min(s_v)(v是u的儿子)su​=min(sv​)(v是u的儿子),也就是儿子共享的部分,这部分给父亲,让父亲赋更大的值,如果叶子所在层也为偶数层的话其 aia_iai​ 值显然赋 0 最优,所以其 sis_isi​ 的值应和父亲的相同,知道了所有结点的 sis_isi​ 值,aia_iai​ 就很好求了

代码

#include #define ft first #define sd second #define pb push_back #define ms(x,y) memset(x,y,sizeof(x)) using namespace std; typedef long long ll; typedef pair P; const int maxn=1e5+7; const int inf=0x3f3f3f3f; int n,flag=0; ll a[maxn],s[maxn]; int dep[maxn]; vector g[maxn]; ll dfs(int u,int fa,ll ma){//ma为父亲的s[]值 dep[u]=dep[fa]+1; ll qq=inf; if(s[u]!=-1){ if(s[u]<ma)flag=1; else ma=s[u]; } for(auto v: g[u]){ if(v==fa)continue; qq=min(dfs(v,u,ma),qq); } if(s[u]==-1&&qq!=inf)s[u]=qq;//是否为叶子 if(s[u]==-1&&qq==inf)s[u]=ma; return s[u]; } void _dfs(int u,int fa,ll ma){ a[u]=s[u]-ma; ma=s[u]; for(auto v: g[u]){ if(v==fa)continue; _dfs(v,u,ma); } } int main() { scanf("%d",&n); for(int u=2,v;u<=n;u++){ scanf("%d",&v); g[u].pb(v); g[v].pb(u); } for(int i=1;i<=n;i++)scanf("%lld",&s[i]); dfs(1,0,0); if(flag){ puts("-1");return 0; } _dfs(1,0,0); ll sum=0; for(int i=1;i<=n;i++){ sum+=a[i]; } cout<<sum<<'\n'; return 0; } E. Nice table (找规律+模拟)

题意

  一个 n×mn×mn×m 矩阵,要求最小的修改次数使得矩阵的所有 2×22×22×2 的子矩阵都包含 A,T,G,CA,T,G,CA,T,G,C 四个字母

思路

找规律会发现有两种情况:

  1.每行都是两个固定字符交替组成,且相邻两行的字符交集为空
例如:
在这里插入图片描述
  2.每列都由两个固定字符交替组成,且相邻两列的字符交集为空
例如:
在这里插入图片描述
那就枚举交替的行或列,找出替换最小的那组

代码

#include #define ft first #define sd second #define pb push_back using namespace std; typedef long long ll; typedef pair P; const int maxn=3e5+7; const double inf=1e18; int n,m,hid,lid; string s[maxn]; char str[6][2]={{'A','T'},{'A','C'},{'A','G'},{'C','T'},{'T','G'},{'C','G'}}; int h[6][maxn][2],hsum[6]; int l[6][maxn][2],lsum[6]; string ans[maxn]; void solve(int op,int id){ if(!op){//行 for(int i=0;i<n;i++){ if(h[id][i][0]!=-1){ for(int j=0;j<m;j++){ printf("%c",str[(i&1)?5-id:id][j&1]); } } else { for(int j=0;j<m;j++){ printf("%c",str[(i&1)?5-id:id][!(j&1)]); } } printf("\n"); } return; } for(int i=0;i<m;i++){//列 if(l[id][i][0]!=-1){ for(int j=0;j<n;j++){ ans[j]+=str[(i&1)?5-id:id][j&1]; } } else { for(int j=0;j<n;j++){ ans[j]+=str[(i&1)?5-id:id][!(j&1)]; } } } for(int i=0;i<n;i++)cout<<ans[i]<>n>>m; for(int i=0;i>s[i]; memset(h,-1,sizeof(h)); memset(l,-1,sizeof(l)); for(int i=0;i<6;i++){//枚举行 int sum=0; for(int j=0;j<n;j++){ int a=0,b=0; for(int k=0;k=b)h[i][j][1]=b,sum+=b;//逆序 else h[i][j][0]=a,sum+=a;//正序 } hsum[i]=sum; if(hsum[hid]>hsum[i])hid=i;//保留最优解下标 } for(int i=0;i<6;i++){//枚举列 int sum=0; for(int j=0;j<m;j++){ int a=0,b=0; for(int k=0;k=b)l[i][j][1]=b,sum+=b;//逆序 else l[i][j][0]=a,sum+=a;//正序 } lsum[i]=sum; if(lsum[lid]>lsum[i])lid=i;//保留最优解下标 } if(hsum[hid]<lsum[lid])solve(0,hid);//行 else solve(1,lid);//列 return 0; } F. Cookies (待补)

题意


思路

代码

二. 二分练习 A. Aggressive cows (最大化最小值)

题目链接

  POJ 2456

题意

  xxx 坐标轴有 nnn 个牛舍,现在给 mmm 头牛分配牛舍,问怎样分配使得任意两头牛的距离最大化

思路

  二分一下任意两头牛的最小距离,从前往后贪心的填即可

代码

#include #include #include #include #include #define ft first #define sd second #define nul string::npos #define ms(x,y) memset(x,y,sizeof(x)) using namespace std; typedef long long ll; typedef pair P; const int maxn=2e5+7; const int inf=0x3f3f3f3f; int t,n,c; int a[maxn]; bool judge(int x){ int pos=a[0]; int co=1; for(int i=1;i<n;i++){ if(a[i]-pos=c; } int main() { cin>>n>>c; for(int i=0;i<n;i++){ scanf("%d",&a[i]); } sort(a,a+n); int l=0,r=inf,ans=1; while(l>1; if(judge(m)){ l=m+1;ans=m; } else r=m-1; } cout<<ans<<'\n'; return 0; } B. String Game (二分下标)

题目链接

  CodeForces - 779D

题意

  一个字符串 sss,现在有一个删除序列,问按照这个删除序列执行,最多执行几次,原序列仍有 ttt 字符串的子序列

思路

  二分这个删除序列的下标,看以这个下标为终点对 sss 串进行删除,剩下的串能否找出一个子序列为 ttt

代码

#include #include #include #include #include #include #define ft first #define sd second #define nul string::npos #define ms(x,y) memset(x,y,sizeof(x)) using namespace std; typedef long long ll; typedef pair P; const int maxn=2e5+7; const int inf=0x3f3f3f3f; int n,c,len1,len2; int a[maxn]; bool vis[maxn]; string s,t; bool judge(int m){ ms(vis,0); int len=min(m,len1); for(int i=1;i<=len;i++){ vis[a[i]]=1; } string no=""; for(int i=0;i<len1;i++){ if(!vis[i+1]){ no+=s[i]; } } len=no.length(); int l=0; for(int i=0;i>s>>t; len1=s.length(); len2=t.length(); for(int i=1;i<=len1;i++)scanf("%d",&a[i]); int l=0,r=len1+1,ans=0; while(l>1; if(judge(m)){ l=m+1,ans=m; } else r=m-1; } ans=min(ans,len1); cout<<ans<<'\n'; return 0; } C. Delivering Carcinogen (二分+计算几何)

题目链接

  CodeForces - 199E

题意

  有一颗行星 PPP 围绕一颗恒星 DDD 以恒定速度 vpv_pvp​ 逆时针旋转,绕行半径为 RRR,现在有一艘飞船(初始位置给出)要去 行星 PPP 上,且要求任何时刻都不能处在 以恒星 DDD 为中心,半径为 rrr 的范围内,让你求到达行星 PPP 的最短时间

思路

  对于一个确定时间 ttt,行星的位置确定,飞船到达行星的时间取决于其运行路线,如果以最短路线前往 PPP,花费时间为 t1t_1t1​,只要 t1t_1t1​ 不大于 ttt,就一定可以到达 PPP,所以可以对时间二分,来寻找最短时间。

对于确定的时间 ttt,现在有几个问题需要解决:

给出行星的初始坐标,经过时间 ttt 后的具体坐标 确定行星的位置后,飞船的最短距离的确定与计算

(1) 对于前者,确定其逆时针旋转多少度即可
推导过程如下:
在这里插入图片描述
设原向量 p1p_1p1​ 坐标 (x1,y1)(x_1,y_1)(x1​,y1​),长度为 RRR ,与 xxx 轴夹角为 AAA,逆时针旋转 BBB 角度后的坐标为 (x2,y2)(x_2,y_2)(x2​,y2​),则有:
x2=Rcos(A+B)x_2=Rcos(A+B)x2​=Rcos(A+B)
=Rcos(A)cos(B)−Rsin(A)sin(B)=Rcos(A)cos(B)-Rsin(A)sin(B)=Rcos(A)cos(B)−Rsin(A)sin(B)
=x1cos(B)−y1sin(B)=x_1cos(B)-y_1sin(B)=x1​cos(B)−y1​sin(B)
y2y_2y2​ 同理:
y2=Rsin(A+B)y_2=Rsin(A+B)y2​=Rsin(A+B)
=Rsin(A)cos(B)+Rcos(A)sin(B)=Rsin(A)cos(B)+Rcos(A)sin(B)=Rsin(A)cos(B)+Rcos(A)sin(B)
=y1cos(B)+x1sin(B)=y_1cos(B)+x_1sin(B)=y1​cos(B)+x1​sin(B)
(2) 对于后者,比较麻烦。要先确定是否可以直线到达,或者是中间需要绕着走,这取决于 飞船与行星的距离两者关于内圆的切线段长度和 谁大谁小
在这里插入图片描述
在这里插入图片描述
  直线走的话就直接拿两点距离除以飞船速度可得最短时间,如果需要绕行的话,需要计算中间那段绕内圆的弧长,弧度的计算是先计算 飞船与行星关于原点的夹角 ,可根据余弦定理来求(飞船 行星和原点组成一个三角形,知道三边可求任意夹角),然后计算 飞船与切点关于圆心的夹角行星与切点关于圆心的夹角,这个很好求。然后求得的大角减去两个小角即为我们要的那段弧的弧度,乘上半径即为距离。
在这里插入图片描述
代码

#include #define ft first #define sd second #define pb push_back using namespace std; typedef long long ll; typedef pair P; const int maxn=2e5+7; const double inf=1e18; const double eps=1e-8; const double pi=acos(-1.0); double xp,yp,vp,x,y,v,r; double tp,R; bool c(double t){ double a=(t-(int)(t/tp)*tp)*pi*2/tp;//行星在t时刻需要逆时针旋转的角度 double nxp=xp*cos(a)-yp*sin(a);//行星的x坐标 double nyp=yp*cos(a)+xp*sin(a);//行星的y坐标 double T=0.0;//飞船沿最短路走花费总时长 double dis1=sqrt(x*x+y*y-r*r);//飞船与内圆的切线长度 double dis2=sqrt(nxp*nxp+nyp*nyp-r*r);//星球与内圆的切线长度 double dis3=sqrt(x*x+y*y);//飞船到原点的距离 double dis4=sqrt(nxp*nxp+nyp*nyp);//行星到原点的距离 就是 R double dis=sqrt((x-nxp)*(x-nxp)+(y-nyp)*(y-nyp));//飞船与行星的距离 if(dis1+dis2>=dis)T=dis/v;//不需要绕行 else {//需要绕行 //余弦定理求大角 double angle=acos((dis3*dis3+dis4*dis4-dis*dis)/(dis3*dis4*2)); double angle1=atan(dis1/r);//飞船的小角 double angle2=atan(dis2/r);//行星的小角 angle-=angle1+angle2; T=(dis1+dis2+angle*r)/v; } return T>xp>>yp>>vp>>x>>y>>v>>r; R=sqrt(xp*xp+yp*yp); tp=pi*R*2.0/vp;//行星周期 double l=0.0,r=inf,ans=0; for(int i=1;i<=100;i++){ double m=(l+r)/2.0; if(c(m)){ r=m;ans=m; } else l=m; } printf("%.10f\n",ans); return 0; }
作者:ぺ晨曦若梦ぺ



CodeForces round div

需要 登录 后方可回复, 如果你还没有账号请 注册新账号