Lyft Level 5 Challenge 2018 - Final Round Div. 1没翻车记
夜晚使人着迷。没有猝死非常感动。
A:显然对于水平线段,只有横坐标的左端点为1的时候才可能对答案产生影响;对于竖直直线,如果要删一定是删去一段前缀。枚举竖直直线删到哪一条,记一下需要删几条水平线段就可以了。想当然的以为竖直直线横坐标是升序排的,因为这个wa了两发感觉非常惨。
#include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; #define ll long long #define N 100010 char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')) c=getchar();return c;} int gcd(int n,int m){return m==0?n:gcd(m,n%m);} int read() { int x=0,f=1;char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();} while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } int n,m,line[N],row[N]; int main() { #ifndef ONLINE_JUDGE freopen("a.in","r",stdin); freopen("a.out","w",stdout); #endif n=read(),m=read(); for (int i=1;i<=n;i++) line[i]=read();line[++n]=1000000000;sort(line+1,line+n+1); int t=0; for (int i=1;i<=m;i++) { int x=read(),y=read();read(); if (x==1) row[++t]=y+1; } sort(row+1,row+t+1); int x=0,cnt=t,ans=n+m; for (int i=1;i<=n;i++) { while (x<t&&row[x+1]<=line[i]) x++,cnt--; ans=min(ans,cnt+i-1); } cout<<ans; return 0; }
B:注意到五次实在是太少了连log都不到,那么看起来一定可以更少。只需要两次询问。第一次在第二个人给出的点中随便选一个,询问该点在第一个人那里的编号。如果该编号在第一个人给出的点中出现,直接输出就可以了;否则注意到树上的点到另一连通块的路径是唯一的,我们只需要从该点沿着树爬,直到第一次进入该连通块,记录所遇到的第一个连通块内的点。那么如果两个连通块有交点,该点一定同时在两个连通块内。所以只要问一下这个点在第二个人那里的编号就可以了。
#include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; #define ll long long #define N 1010 char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')) c=getchar();return c;} int gcd(int n,int m){return m==0?n:gcd(m,n%m);} int read() { int x=0,f=1;char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();} while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } int T,n,p[N],k1,k2,a[N],b[N],flag[N],flag2[N],t; struct data{int to,nxt; }edge[N<<1]; void addedge(int x,int y){t++;edge[t].to=y,edge[t].nxt=p[x],p[x]=t;} int dfs(int x,int from) { if (flag[x]) return x; for (int i=p[x];i;i=edge[i].nxt) if (edge[i].to!=from) { int y=dfs(edge[i].to,x); if (y) return y; } return 0; } int main() { #ifndef ONLINE_JUDGE freopen("a.in","r",stdin); freopen("a.out","w",stdout); #endif T=read(); while (T--) { n=read();memset(p,0,sizeof(p));t=0; for (int i=1;i<n;i++) { int x=read(),y=read(); addedge(x,y),addedge(y,x); }memset(flag,0,sizeof(flag));memset(flag2,0,sizeof(flag2)); k1=read(); for (int i=1;i<=k1;i++) flag[read()]=1; k2=read(); for (int i=1;i<=k2;i++) b[i]=read(),flag2[b[i]]=1; cout<<'B'<<' '<<b[1]<<endl; int x=read(); if (flag[x]) cout<<'C'<<' '<<x<<endl; else { int y=dfs(x,x); cout<<'A'<<' '<<y<<endl; int p=read(); if (flag2[p]) cout<<'C'<<' '<<y<<endl; else cout<<'C'<<' '<<-1<<endl; } } return 0; }
这个时候花掉了50min,处于A两题的最后一名,感觉掉rating非常稳了。C看上去非常可怕,看了看榜发现不少人莫名其妙的只过了D,于是赶紧去看。诶这个东西我是不是见过啊?https://www.cnblogs.com/Gloid/p/9723734.html 于是非常开心了感觉能翻啊。
D:每次获得的信息可以看做是两个前缀和的异或。如果要知道某段区间的异或值也即该两个前缀和的异或值,这两个前缀必须通过已给的信息连通。LCT维护一发路径异或值就可以了。事实上cf好像很少有高级数据结构题,所以其实带权并查集就可以维护了,感觉自己是弱智。
#include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> #include<map> using namespace std; #define ll long long #define N 200010 #define lson tree[k].ch[0] #define rson tree[k].ch[1] #define lself tree[tree[k].fa].ch[0] #define rself tree[tree[k].fa].ch[1] char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')) c=getchar();return c;} int gcd(int n,int m){return m==0?n:gcd(m,n%m);} int read() { int x=0,f=1;char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();} while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } map<int,int> id; int q,lastans,cnt; struct data{int x,fa,ch[2],rev,sum; }tree[N*3]; void up(int k){tree[k].sum=tree[lson].sum^tree[rson].sum^tree[k].x;} void rev(int k){if (k) swap(lson,rson),tree[k].rev^=1;} void down(int k){if (tree[k].rev) rev(lson),rev(rson),tree[k].rev=0;} bool isroot(int k){return lself!=k&&rself!=k;} void push(int k){if (!isroot(k)) push(tree[k].fa);down(k);} int whichson(int k){return rself==k;} void move(int k,int p) { int fa=tree[k].fa,gf=tree[fa].fa; if (!isroot(fa)) tree[gf].ch[whichson(fa)]=k;tree[k].fa=gf; tree[fa].ch[p]=tree[k].ch[!p],tree[tree[fa].ch[p]].fa=fa; tree[k].ch[!p]=fa,tree[fa].fa=k; up(fa),up(k); } void splay(int k) { push(k); while (!isroot(k)) { int fa=tree[k].fa; if (!isroot(fa)) if (whichson(k)^whichson(fa)) move(k,whichson(k)); else move(fa,whichson(k)); move(k,whichson(k)); } } void access(int k){for (int t=0;k;t=k,k=tree[k].fa) splay(k),tree[k].ch[1]=t,up(k);} int findroot(int k){access(k),splay(k);for (;lson;k=lson) down(k);splay(k);return k;} void makeroot(int k){access(k),splay(k),rev(k);} int query(int x,int y){makeroot(x),access(y),splay(y);return tree[y].sum;} void link(int x,int y) { if (findroot(x)!=findroot(y)) makeroot(x),tree[x].fa=y; } int main() { #ifndef ONLINE_JUDGE freopen("b.in","r",stdin); freopen("b.out","w",stdout); #endif q=read(); while (q--) { int op=read(); if (op==1) { int l=read()^lastans,r=read()^lastans,x=read()^lastans; if (l>r) swap(l,r); if (!id[l-1]) id[l-1]=++cnt; if (!id[r]) id[r]=++cnt; int p=id[l-1],q=id[r]; if (findroot(p)==findroot(q)) ; else cnt++,tree[cnt].x=x,link(cnt,p),link(cnt,q); } else { int l=read()^lastans,r=read()^lastans; if (l>r) swap(l,r); int p=id[l-1],q=id[r]; if (!p||!q||findroot(p)!=findroot(q)) lastans=-1; else lastans=query(p,q); printf("%d\n",lastans); if (lastans==-1) lastans=1; } } return 0; }
然后滚去看C。没什么思路于是度娘一发看有没有什么启发性的东西,突然就找到一个类似曼哈顿距离之和就是矩形周长。诶那k>=4就做完了啊?然后冷静了好长时间去想k=3最后总算还是搞出来了。
C:首先注意到所要求的曼哈顿距离之和就是所选出的点的值域所构成的矩形的周长。那么对于k>=4,横坐标min、横坐标max、纵坐标min、纵坐标max就一定可以取到了。对于k=3,由抽屉原理,有两个点是只提供矩形的一个边界的,那么他们一定在取到极值时最优。所以暴力枚举提供至少两个边界的点,再枚举一下剩下的两个点提供的是哪项边界就可以了。写的时候写丑了或者说当时不是这么想的。
#include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; #define ll long long #define N 300010 char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')) c=getchar();return c;} int gcd(int n,int m){return m==0?n:gcd(m,n%m);} int read() { int x=0,f=1;char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();} while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } int n,ans,sufmax[N],sufmin[N]; struct data{int x,y; }a[N]; bool cmp(const data&a,const data&b) { return a.x<b.x; } bool cmp2(const data&a,const data&b) { return a.y<b.y; } int main() { #ifndef ONLINE_JUDGE freopen("a.in","r",stdin); freopen("a.out","w",stdout); #endif n=read(); for (int i=1;i<=n;i++) a[i].x=read(),a[i].y=read(); int l=a[1].x,r=a[1].x,u=a[1].y,d=a[1].y; for (int i=2;i<=n;i++) { l=min(l,a[i].x); r=max(r,a[i].x); u=max(u,a[i].y); d=min(d,a[i].y); } ans=(r-l+u-d)*2; int ans2=0; sort(a+1,a+n+1,cmp); sufmax[n]=a[n].y,sufmin[n]=a[n].y; for (int i=n-1;i>=1;i--) sufmax[i]=max(sufmax[i+1],a[i].y),sufmin[i]=min(sufmin[i+1],a[i].y); for (int i=1;i<n;i++) ans2=max(ans2,(a[n].x-a[i].x+max(sufmax[i]-min(a[i].y,a[n].y),max(a[i].y,a[n].y)-sufmin[i]))<<1); for (int i=1;i<=n;i++) swap(a[i].x,a[i].y); sort(a+1,a+n+1,cmp); sufmax[n]=a[n].y,sufmin[n]=a[n].y; for (int i=n-1;i>=1;i--) sufmax[i]=max(sufmax[i+1],a[i].y),sufmin[i]=min(sufmin[i+1],a[i].y); for (int i=1;i<n;i++) ans2=max(ans2,(a[n].x-a[i].x+max(sufmax[i]-min(a[i].y,a[n].y),max(a[i].y,a[n].y)-sufmin[i]))<<1); for (int i=1;i<=n;i++) a[i].x=-a[i].x; sort(a+1,a+n+1,cmp); sufmax[n]=a[n].y,sufmin[n]=a[n].y; for (int i=n-1;i>=1;i--) sufmax[i]=max(sufmax[i+1],a[i].y),sufmin[i]=min(sufmin[i+1],a[i].y); for (int i=1;i<n;i++) ans2=max(ans2,(a[n].x-a[i].x+max(sufmax[i]-min(a[i].y,a[n].y),max(a[i].y,a[n].y)-sufmin[i]))<<1); for (int i=1;i<=n;i++) a[i].x=-a[i].x; for (int i=1;i<=n;i++) swap(a[i].x,a[i].y); for (int i=1;i<=n;i++) a[i].x=-a[i].x; sort(a+1,a+n+1,cmp); sufmax[n]=a[n].y,sufmin[n]=a[n].y; for (int i=n-1;i>=1;i--) sufmax[i]=max(sufmax[i+1],a[i].y),sufmin[i]=min(sufmin[i+1],a[i].y); for (int i=1;i<n;i++) ans2=max(ans2,(a[n].x-a[i].x+max(sufmax[i]-min(a[i].y,a[n].y),max(a[i].y,a[n].y)-sufmin[i]))<<1); cout<<ans2<<' '; for (int i=4;i<=n;i++) printf("%d ",ans); return 0; }
历史总是惊人的相似?但是感觉并不能在这个分数稳住啊。
result:rank 69 rating +70