2020ICPC上海站总结&补题
这场我们队只过了两道题,五个小时一直自闭中。
B题思维题,没想出来
C题数位DP,比赛的时候没仔细看题,觉得太复杂就没看
D题分类讨论加二分,捋不清楚分哪几种
G题签到题
I题读错了,wa在了m==1的情况,m==1时,圆心点不计入
L题读完题就在想一个最优解,结果做法是枚举有可能是最优的点,线性复杂度
M题签到题,队友说有思路,我就直接看别的题去了,结果调了挺久
B题:
题意:
给一个n、m,以及两个n×m的图,X代表周围地雷的个数,.代表地雷。
将第二个图修改(.变为X或X变为.)至多m*n/2次使得X的值的总和等于第一个图的X的值的总和
输出修改后的图
思路:
X的值的综合即为每个九宫格中X与.的对数之和
将X变换为.,.变换为X,X的值的总和不变。
故做法为变换不超过m*n/2次数下,修改成图1的原图或图一.与X互换后的图
代码:
#include<bits/stdc++.h> using namespace std; char a[1005][1005],b[1005][1005]; int main(){ int n,m; cin>>n>>m; for(int i=0;i<n;i++){ for(int j=0;j<m;j++){ cin>>a[i][j]; } } int diff=0; for(int i=0;i<n;i++){ for(int j=0;j<m;j++){ cin>>b[i][j]; if(b[i][j]!=a[i][j]) diff++; } } if(diff>n*m/2){ for(int i=0;i<n;i++){ for(int j=0;j<m;j++){ a[i][j]=(a[i][j]=='X'?'.':'X'); } } } for(int i=0;i<n;i++){ for(int j=0;j<m;j++){ cout<<a[i][j]; } cout<<endl; } }
D题:
在一个长为n的线段,左端点为0,右端点为n
有两个端点分别位于p1,p2,它们的速度分别为v1,v2
问最少需要多少时间,可以使得p1,p2的路程覆盖整条线段
思路:
分类讨论:
先令p1<p2
①p1走完全程
②p2走完全程
③p1向右走完全部,p2向左走完全部
④p1走完左边的全部,p2走完右边的全部,剩余中间的部分p1与p2共同走完,二分路程或时间即可
代码:
#include<bits/stdc++.h> using namespace std; const double eps=1e-8; double n,p1,p2,v1,v2; int main(){ int t; cin>>t; while(t--){ cin>>n>>p1>>v1>>p2>>v2; if(p1>p2){swap(p1,p2);swap(v1,v2);}; //p1走完全程 double ans=min(p1+n,2*n-p1)/v1; //p2走完全程 ans=min(ans,min(2*n-p2,p2+n)/v2); //p1走完右边全程,p2走完左边全程 ans=min(ans,max((n-p1)/v1,p2/v2)); //p1走完左边全程,p2走完右边全程,二分分界点 double l=p1,r=p2; while(r-l>eps){ double mid=(l+r)/2; double ans1=min(p1+mid,2*mid-p1)/v1,ans2=min(2*n-p2-mid,p2+n-2*mid)/v2; ans=min(ans,max(ans1,ans2)); if(ans1<ans2)//p1先走完 分界点向右移 l=mid; else r=mid; } printf("%.10lf\n",ans); } }
G题:
签到题
题意:
对于斐波那契数列 1、1、2、3、5、8、13、21、34……
给一个n计算所有g(fi,fj)的和(i<j)
对于a和b,当a*b为偶数时g(a,b)=1,否则g(a,b)=0;
思路:
观察斐波那契数列易得,当i%3==0时fi为偶数
当两个数都不是奇数时,两个数乘积为偶数
从数列中选出两对共有C2(n)种选法
偶数的个数有n/3个 则奇数的个数有n-n/3
故答案为C2(n)-C2(n-n/3)
代码:
#include<bits/stdc++.h> #define ll long long using namespace std; ll C2(ll a){ return a*(a-1)/2; } int main(){ ll n; cin>>n; ll m=n/3; cout<<C2(n)-C2(n-m)<<endl; }
I题:
给一个n和m。
有n个圆心在(0,0)的圆,第i个圆的半径为i
有m条经过(0,0)的线将这些圆分为等份
线与线、线与圆会产生交点,求这些交点两两之间的最短路径和
思路:
对于外层圆上的点到内层圆上的点的最短路径必然要先向圆心走到同层的位置
故可以通过递推的方式来做
唯一要注意的是m=1时不包括圆心,m>1时包括了圆心
代码:
#include<bits/stdc++.h> using namespace std; #define ll long long #define fastio ios::sync_with_stdio(false),cin.tie(NULL),cout.tie(NULL) const int maxn = 1e6 + 10; const double PI=acos(-1); double a[505],b[505]; int n,m; int main() { fastio; cin>>n>>m; double cnt=0; for(int i=1;i<m;i++){ if(PI*i<2*m) cnt+=PI*i/m; else cnt+=2; //cout<<cnt<<endl; } cnt*=2; cnt+=2; a[1]=cnt;b[1]=a[1]; //cout<<a[1]<<endl; for(int i=2;i<=n;i++){ a[i]=a[i-1]+(i-1)*(2*m)+i*b[1]; b[i]=i*b[1]; } double ans=0; for(int i=1;i<=n;i++){ ans+=2*m*(a[i]-b[i])+m*b[i]; if(m>1) ans+=2*i*m; } printf("%.10lf\n",ans); return 0; }
L题:
题意:
有一个网格图n×m
左下点为(0,0),右上点为(n,m)
从一个点可以到达任意点,但路径不能出现其他点(或者说,不能出现两条路径是相连接的)
求从(0,0)到(n,m)的最短路径
思路:
如果gcd(n,m)==1,则从(0,0)到(n,m)上无其他点,最短路径为√(n2+m2)
否则,路径分为两段 从(0,0)到(x,y),从(x,y)到(n,m)
枚举每个x,对于每个x,y的值在x*m/n附近取值一定是最优的
代码:
#include<bits/stdc++.h> #define ll long long using namespace std; ll gcd(ll a,ll b){ return a%b?gcd(b,a%b):b; } int main(){ ll t,n,m; cin>>t; while(t--){ cin>>n>>m; double ans; if(gcd(n,m)==1){ ans=sqrt(n*n+m*m); }else{ ans=1e15; for(ll x=1;x<n;x++){ ll y=x*m/n,y1,y2; if(x*m==y*n){y1=y-1,y2=y+1;} else{y1=y,y2=y+1;} if(gcd(y1,x)==1&&gcd(m-y1,n-x)==1){ ans=min(ans,sqrt(x*x+y1*y1)+sqrt((n-x)*(n-x)+(m-y1)*(m-y1))); } if(gcd(y2,x)==1&&gcd(m-y2,n-x)==1){ ans=min(ans,sqrt(x*x+y2*y2)+sqrt((n-x)*(n-x)+(m-y2)*(m-y2))); } } } printf("%.15lf\n",ans); } }
M题: