10.16考试反思
Contest2171 - 2019年10月多校联训B层测试12
T1 妹子
http://www.accoders.com/problem.php?cid=2171&pid=0
T1看似是个非常弱智的题,实际上非常具有思考价值,不光需要考虑每个边的关系,还需要考虑这个盒子能不能斜着放到另一个盒子里,如果没判,就很GG的70分
1 #include <bits/stdc++.h> 2 #define ll long long 3 #define res register 4 #define MAXN 200050 5 #define int ll 6 #define db double 7 int n,m,a,b,c,d; 8 double k1,k2; 9 using namespace std; 10 ll read() 11 { 12 int s=0,w=1;char ch=getchar(); 13 while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();} 14 while(ch>='0'&&ch<='9'){s=(s<<1)+(s<<3)+ch-48;ch=getchar();} 15 return s*w; 16 } 17 signed main() 18 { 19 n=read(); 20 for(res int i=1;i<=n;i++){ 21 a=read(),b=read(),c=read(),d=read(); 22 k1=sqrt(a*a*1.0+1.0*b*b);k2=sqrt(c*c*1.0+d*d*1.0); 23 if(b<a) std::swap(a,b);if(d<c) std::swap(c,d); 24 if(k1<k2) std::swap(k1,k2),swap(a,c),swap(b,d); 25 if(k2<b) {if(a>=c) cout<<"Yes"<<endl;else cout<<"No"<<endl;} 26 else{ 27 double k=sqrt(k2*k2-b*b*1.0); 28 if(k<=c){if(a>=c) cout<<"Yes"<<endl;else cout<<"No"<<endl;continue;} 29 db x=(k+a*1.0)/2,y=(a*1.0-k)/2; 30 db kk=sqrt(d*d*1.0-x*x); 31 db r=(b*1.0-kk); 32 if(r*r+y*y<c*c) puts("No");else puts("Yes"); 33 } 34 } 35 }
T2 旅程
http://www.accoders.com/problem.php?cid=2171&pid=1
首先看数据范围n<=200,还需要求最短路,显然是Floyd没跑了。然后我们考虑对于修改进行离线处理,然后修改,正序删边就相当于倒叙加边,我们考虑对于x-y的最短路,我们考虑,如果加上一条边之后,两点之间的最短路发生变化,显然是x-i的最短路i-j的最短路以及j-y的最短路之和,所以我们可以考虑枚举i和j,枚举之后O(1)查询,然后跟已有的最短路比较,如果优,就更新即可
#include <bits/stdc++.h> #define ll long long #define res register #define MAXN 300050 #define int ll using namespace std; bool ju; int x,y,z,n,m,w[MAXN],diss[2500][2500],as,op[200050],gg[2500][2500],top,xx[200050],yy[200050],cnt,ans[MAXN]; inline void Floyd(){ for(res int k=1;k<=n;k++) for(res int i=1;i<=n;i++) for(res int j=1;j<=n;j++) { if(diss[i][j]>diss[i][k]+diss[k][j]) diss[i][j]=diss[i][k]+diss[k][j]; } } inline void rebuild(){ for(res int i=1;i<=n;i++) for(res int j=1;j<=n;j++) if(gg[i][j]==0) diss[i][j]=0x7f7f7f; else diss[i][j]=gg[i][j]; } ll read() { int s=0,w=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();} while(ch>='0'&&ch<='9'){s=(s<<1)+(s<<3)+ch-48;ch=getchar();} return s*w; } signed main() { n=read(),m=read(); for(res int i=1;i<=n;i++) for(res int j=1;j<=n;j++){ gg[i][j]=read(); if(gg[i][j]==0) diss[i][j]=0; else diss[i][j]=gg[i][j]; } for(res int i=1;i<=m;i++) { op[i]=read(),xx[i]=read();yy[i]=read(); if(op[i]==1) diss[xx[i]][yy[i]]=0x3f3f3f3f,w[i]=gg[xx[i]][yy[i]]; } Floyd(); for(res int i=m;i>=1;i--){ int dx=xx[i],dy=yy[i]; if(op[i]==2) ans[++cnt]=diss[dx][dy]; else for(res int j=1;j<=n;j++) for(res int k=1;k<=n;k++){ int dis=diss[j][dx]+diss[dy][k]+w[i]; if(dis<diss[j][k]) diss[j][k]=dis; } } for(res int i=cnt;i>=1;i--) printf("%lld\n",ans[i]>=0x3f3f3f3f?-1:ans[i]); return 0; }
总结,首先对于正序删边的问题,我们可以考虑将其转化为倒叙加边的问题,这样会更好处理,然后对于这种带修改的问题,我们往往会选择离线的手段解决问题。然后由Floyd的性质可以n3求出一个图的任意两点最短路,我们就方便每次n2枚举O1查询了
T3 老大
http://www.accoders.com/problem.php?cid=2171&pid=2
首先审题,需要最大值最小,我们第一思路就是二分然后审题,我们需要把两个奖杯放在不同位置,显然就是放在树的直径上最优,我们就可以先求出树的直径,然后二分答案,最后取离直径上离端点距离答案的点,遍历 check 一遍。时间复杂度nlogn
#include <bits/stdc++.h> #define ll long long #define res register #define MAXN 100050 #define int ll using namespace std; int deep[MAXN],head[MAXN<<2],x,y,n,m,maxn1,maxn2,root1,ans1,siz[MAXN],fa[MAXN][20],root2,ans2; int dl,o,r1,r2,num[200500],now; struct Node{ int to,nxt; }g[MAXN<<4]; inline void dfs(int u,int fa,int d){ if(d>dl){dl=d,o=u;} for(res int i=head[u];i;i=g[i].nxt){ int to=g[i].to;if(to==fa) continue; dfs(to,u,d+1); } } inline void add(int u,int v){ static int top=0; g[++top].to=v;g[top].nxt=head[u]; head[u]=top; } inline void mark(int u,int fa,int qwq){ if(!qwq||qwq<=num[u]) return ; if(!num[u]) now++; num[u]=qwq; for(res int i=head[u];i;i=g[i].nxt){ int to=g[i].to;if(to==fa) continue; mark(to,u,qwq-1); } } inline bool check(int x){ now=0; memset(num,0,sizeof(num)); mark(r1,0,x<<1|1); mark(r2,0,x<<1|1); return now==n; } inline int erfen(int l,int r){ if(l==r) return l; int mid=l+r>>1; if(check(mid)) return erfen(l,mid); else return erfen(mid+1,r); } ll read() { int s=0,w=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();} while(ch>='0'&&ch<='9'){s=(s<<1)+(s<<3)+ch-48;ch=getchar();} return s*w; } signed main() { n=read(); for(res int i=1;i<n;i++){ x=read(),y=read(); add(x,y);add(y,x); } dfs(1,0,1);r1=o;dl=0; dfs(r1,0,1);r2=o; cout<<erfen(0,1e6)<<endl; return 0; }
总结,对于在树上任意选几个点,使得每个点到这几个选中的点的距离最大值最小,是标准的二分模型,我们在考虑奖杯放置的位置,其实有讲究,是一定在树的直径上更优秀的,然后我们考虑在树的直径上二分答案,就可以优秀的求出答案了