考试多思考,认真对待每场考试。
仍旧一个问题,考到一半就放弃了。主要是怕打不完,T2只码了个暴力,T1没有深度思考,只是在皮上画画写写,也许这种情况下已经没法思考了。下次考试看看能不能静下心来思考,不要慌。
T1:很好的一道题,想到了要判是不是一些块把某一条路封死了,但是我一想它可能不是一条竖线上就觉得比较麻烦,横着错开比较难判,但是图论就是干这个的,你把所有点向上下边界建边,上边界到下边界的最大值最小的一条路径就是瓶颈,走的时候总会穿过一条最长的路径。这就不用看点与点是不是在一条竖线上,其实是我们不关心它的位置,我只关心点与点的距离,然后这样不关心在坐标轴位置的东西,图论建成边,就能只考虑相对位置了。
#include<iostream> #include<cstdio> #include<cmath> #include<queue> #include<cstring> using namespace std; const int N=6020; double d[N]; bool v[N]; int pr[N],n,m,k; struct point{double x,y;}p[N]; inline int rd() { int s=0,w=1; char cc=getchar(); for(;cc<'0'||cc>'9';cc=getchar())if(cc=='-') w=-1; for(;cc>='0'&&cc<='9';cc=getchar()) s=(s<<3)+(s<<1)+cc-'0'; return s*w; } inline double cal(int i,int j) { if(i==k&&j==k-1) return 0x7fffffff; if(j==k&&i==k-1) return 0x7fffffff; if(i==k) return m-p[j].y; if(j==k) return m-p[i].y; if(i==k-1) return p[j].y; if(j==k-1) return p[j].y; point a=p[i],b=p[j]; return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)); } inline void prim() { for(int i=1;i<=k;i++) d[i]=0x7fffffff; memset(v,0,sizeof(v)); d[k-1]=0; for(int i=1;i<=k;i++) { int x=0; for(int j=1;j<=k;j++) if(!v[j]&&(x==0||d[j]<d[x])) x=j; v[x]=1; for(int j=1;j<=k;j++) if(!v[j]) { if(cal(x,j)<d[j]) { d[j]=cal(x,j); pr[j]=x; } } } } int main() { n=rd();m=rd();k=rd(); for(int i=1;i<=k;i++) { double x=rd(),y=rd(); p[i]=(point){x,y}; } k+=2; prim(); int tmp=k; double ans=-0x7fffffff; while(tmp!=k-1) { ans=max(ans,d[tmp]); tmp=pr[tmp]; } printf("%.10lf ",ans/2); } /* g++ -std=c++11 1.cpp -o 1 ./1 10 5 2 1 1 2 3 */
T2:听说是个套路,我用最坏$O(n^{2}logn)$的线段树卡过了。线段树维护单调栈先扔一发
#include<iostream> #include<cstdio> #include<cstring> using namespace std; const int N=200020,inf=1e9; int p[N],f[N],c[N]; struct tree{int l,r,w,i,m;}tr[N*4]; inline int rd() { int s=0,w=1; char cc=getchar(); for(;cc<'0'||cc>'9';cc=getchar()) if(cc=='-')w=-1; for(;cc>='0'&&cc<='9';cc=getchar()) s=(s<<3)+(s<<1)+cc-'0'; return s*w; } void build(const int k,const int l,const int r) { if(l==r) { tr[k].w=inf; return; } int mid=l+r>>1; build(k<<1,l,mid);build(k<<1|1,mid+1,r); } int query(const int k,const int x,const int y,const int l,const int r) { const int mid=l+r>>1; if(l==x&&r==y) return tr[k].i; if(y<=mid) return query(k<<1,x,y,l,mid); else if(x>mid) return query(k<<1|1,x,y,mid+1,r); return max(query(k<<1,x,mid,l,mid),query(k<<1|1,mid+1,y,mid+1,r)); } int ask(const int k,const int x,const int y,const int i,const int l,const int r) { const int mid=l+r>>1; if(l==r) { if(tr[k].i<i) return inf; return tr[k].w; } if(y<=mid) return ask(k<<1,x,y,i,l,mid); else if(x>mid) return ask(k<<1|1,x,y,i,mid+1,r); if(i>tr[k<<1|1].i) return ask(k<<1,x,mid,i,l,mid); const int rmax=query(k,mid+1,y,l,r); return min(ask(k<<1,x,mid,rmax,l,mid),ask(k<<1|1,mid+1,y,i,mid+1,r)); } void updata(const int k){tr[k].i=max(tr[k<<1].i,tr[k<<1|1].i);} void add(const int k,const int id,const int i,const int l,const int r) { const int mid=l+r>>1; if(l==r) { tr[k].i=i; tr[k].w=f[i]; return; } if(id<=mid) add(k<<1,id,i,l,mid); else add(k<<1|1,id,i,mid+1,r); updata(k); } int main() { const int n=rd(); for(register int i=1;i<=n;i++) p[i]=rd(); for(register int i=1;i<=n;i++) c[i]=rd(); build(1,1,n+1); memset(f,0x3f,sizeof(f)); f[0]=0;add(1,1,0,1,n+1); for(register int i=1;i<=n;i++) { const int maxi=0; f[i]=ask(1,1,p[i]+1,0,1,n+1)+c[i]; add(1,p[i]+1,i,1,n+1); } register int ans=0x7fffffff,maxi=0; for(register int i=n;i>=1;i--) { if(p[i]>maxi) { ans=min(ans,f[i]); maxi=p[i]; } } printf("%d ",ans); } /* g++ -std=c++11 1.cpp -o 1 ./1 5 3 1 4 5 2 3 4 3 4 1 */
T3:好题,斜率优化,维护下凸包,skyh好像用的栈,加上启发式合并复杂度很优秀,粘一发题解就跑,然而我在考场上觉得用真栈没什么必要,然后就打的链表模拟栈,链表记录某个元素在它和它的祖先链组成的单调栈中的上一个元素是谁。显然这样可以避免某些重复的情况,然后就可以用父亲更新儿子,显然答案的最优决策点是它的祖先链上在坐标系上形成的下凸包上离他最近的点,然后就用父亲的信息,一直往前跳就行了。但是考场上不会证复杂度,好像单调栈是$O(n)$的,好像可过??然后就死了。其实是一条链上$O(n)$然而精心构造的数据可能卡爆,考完突然被yxm提醒一下这个可以倍增。然后就A了。
#include<iostream> #include<cstdio> #include<cstring> using namespace std; const int N=200020,inf=1e9; int p[N],f[N],c[N]; struct tree{int l,r,w,i,m;}tr[N*4]; inline int rd() { int s=0,w=1; char cc=getchar(); for(;cc<'0'||cc>'9';cc=getchar()) if(cc=='-')w=-1; for(;cc>='0'&&cc<='9';cc=getchar()) s=(s<<3)+(s<<1)+cc-'0'; return s*w; } void build(const int k,const int l,const int r) { if(l==r) { tr[k].w=inf; return; } int mid=l+r>>1; build(k<<1,l,mid);build(k<<1|1,mid+1,r); } int query(const int k,const int x,const int y,const int l,const int r) { const int mid=l+r>>1; if(l==x&&r==y) return tr[k].i; if(y<=mid) return query(k<<1,x,y,l,mid); else if(x>mid) return query(k<<1|1,x,y,mid+1,r); return max(query(k<<1,x,mid,l,mid),query(k<<1|1,mid+1,y,mid+1,r)); } int ask(const int k,const int x,const int y,const int i,const int l,const int r) { const int mid=l+r>>1; if(l==r) { if(tr[k].i<i) return inf; return tr[k].w; } if(y<=mid) return ask(k<<1,x,y,i,l,mid); else if(x>mid) return ask(k<<1|1,x,y,i,mid+1,r); if(i>tr[k<<1|1].i) return ask(k<<1,x,mid,i,l,mid); const int rmax=query(k,mid+1,y,l,r); return min(ask(k<<1,x,mid,rmax,l,mid),ask(k<<1|1,mid+1,y,i,mid+1,r)); } void updata(const int k){tr[k].i=max(tr[k<<1].i,tr[k<<1|1].i);} void add(const int k,const int id,const int i,const int l,const int r) { const int mid=l+r>>1; if(l==r) { tr[k].i=i; tr[k].w=f[i]; return; } if(id<=mid) add(k<<1,id,i,l,mid); else add(k<<1|1,id,i,mid+1,r); updata(k); } int main() { const int n=rd(); for(register int i=1;i<=n;i++) p[i]=rd(); for(register int i=1;i<=n;i++) c[i]=rd(); build(1,1,n+1); memset(f,0x3f,sizeof(f)); f[0]=0;add(1,1,0,1,n+1); for(register int i=1;i<=n;i++) { const int maxi=0; f[i]=ask(1,1,p[i]+1,0,1,n+1)+c[i]; add(1,p[i]+1,i,1,n+1); } register int ans=0x7fffffff,maxi=0; for(register int i=n;i>=1;i--) { if(p[i]>maxi) { ans=min(ans,f[i]); maxi=p[i]; } } printf("%d ",ans); } /* g++ -std=c++11 1.cpp -o 1 ./1 5 3 1 4 5 2 3 4 3 4 1 */