[考试反思]0917csp-s模拟测试45:天命
又倒一了。
关于心态,有不少想说的。
首先旁边坐了一个kx。他上来入手T1没多久就切了然后开始对拍拍了几十万组AC。
然而我觉得T1是神仙题。先进T2。
挺简单的,5分钟出正解,然后在打出来的时候突然有了个主意,感觉是等价的,就按照后来的那种想法打了。
然而根本就没有想,其实后者是错的,错的极其可笑,随随便便就能hack。
然而我没有证明也没有hack,就把正解埋没了。
大样例一过就交了,果断爆零。
这是后skyh开始犯贱,开始瞅着别人笑,我和mikufun鄙视了他一下后继续答题。
然后进T3。显然是个数据结构题。
思路还比较好想,花了15分钟想就想出来了。
但是瞬间就发现这题用的知识点有点多:链修改,求两点间最大边权,最小生成树。
也就是lct/树链剖分+克鲁斯卡尔。。。我脑子抽了还倍增了一下lca以及求距离
剩的时间比较多,我有一个致命的想法:
我平时考场上基本没A掉数据结构题过,这样下去不利于发展。
于是我毅然觉然地走上了正解的道路。
二营长虚了把空调关了,然后我本来就离空调远,特别热。。。
然后越打越头晕,感觉体温都上来了,而且看屏幕都逐渐有了重影(其实是屏幕的问题)
换了个稍硬的键盘敲不动,打各种板子的过程极其痛苦。。。
不管怎么说,2个小时,打出来了,过掉了大样例,心情极佳。
我感觉这是我为数不多能在考场上干掉的大型数据结构题。
然后数据出锅了,只有10分。
也感谢数据没有特别狗屎还给我留下了10分,不然我这场考试就爆零了。
考场上的我并不知道,我以为我A掉2个了。我感觉我状态不错,虽然头晕眼花满头大汗。
还有一个小时给T1,我一想kx在我旁边几十分钟就切了T1,我感觉我应该也可以。
然而感觉自己状态已经抗不住了,先出去透了个风,散散热,缓了一下脑子。
但是还是不会,一如既往的想用网络流乱搞二分图匹配。
按照数据范围说应该是60分?那么260应该不会太低。
所以不再冒着风险想正解了,开始调网络流。
死循环,调了一阵,过了大样例。交了,70分。
感觉数组开大了怕MLE,于是开小一点,再试了一遍大样例,再交。
这一次忘了删freopen了,用0分把70分盖掉了。
结束了,总分10分。
心态还是不行:期望太高了。考场上千万不能盲目自信。
然后还有在最后几分钟着急容易致错。这已经是第二次freopen出锅了。
另外题目的难易度判断也有问题。先T3后T1是个巨大的失误。。。
不要相信大样例!!!一定要对拍!!!
不要想太多。得分高就是王道。
就这样吧。
T1:kill
据说可以nlog做,不会。
两种n2都很好想。但都需要先发现一个性质。
一个性质是打的怪是连续的区间,枚举第一个打的即可。
另一个性质是按位置排序后,每个人打的每个怪的位置是递增的。
两个性质都很好证,但是考场上发现也是不容易。
然后就可以dp乱搞了。
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 using namespace std; 5 int abs(int p){return p>0?p:-p;} 6 int dp[5005][5005],n,m,s,p[5005],q[5005]; 7 int main(){ 8 scanf("%d%d%d",&n,&m,&s); 9 for(int i=1;i<=n;++i)scanf("%d",&p[i]); 10 for(int i=1;i<=m;++i)scanf("%d",&q[i]); 11 sort(p+1,p+1+n);sort(q+1,q+1+m);memset(dp,0x3f,sizeof dp); 12 for(int i=0;i<=m;++i)dp[i][0]=0; 13 for(int i=1;i<=m;++i)for(int j=1;j<=n;++j) 14 dp[i][j]=min(dp[i-1][j],max(dp[i-1][j-1],abs(p[j]-q[i])+abs(s-q[i]))); 15 printf("%d\n",dp[m][n]); 16 }
1 #include<cstdio> 2 #include<iostream> 3 using namespace std; 4 int fir[10005],l[25000005],to[25000005],cnt,n,m,p[5005],q[5005],s,dep[10005]; 5 bool v[25000005];int qu[10005]; 6 int abs(int p){return p>0?p:-p;} 7 void link(int a,int b,int vv){ 8 l[++cnt]=fir[a];fir[a]=cnt;to[cnt]=b;v[cnt]=vv; 9 } 10 bool bfs(){ 11 int top=1;for(int i=1;i<=n+m+1;++i)dep[i]=-1; 12 for(int qh=1;qh<=top;++qh)for(int i=fir[qu[qh]];i;i=l[i])if(v[i]&&dep[to[i]]==-1) 13 dep[to[i]]=dep[qu[qh]]+1,qu[++top]=to[i]; 14 return dep[n+m+1]!=-1; 15 } 16 int dfs(int p,int flow){//printf("%d %d\n",p,flow); 17 if(p==n+m+1)return flow; 18 int res=flow; 19 for(int i=fir[p];i;i=l[i])if(dep[to[i]]==dep[p]+1&&v[i]&&res){ 20 int acs=dfs(to[i],1); 21 if(!acs){dep[to[i]]=0;continue;} 22 v[i]=0;v[i^1]=1;res--; 23 } 24 return flow-res; 25 } 26 bool chk(int tim){ 27 int maxflow=0; 28 for(int i=0;i<=n+m+1;++i)fir[i]=0;cnt=1; 29 for(int i=1;i<=n;++i)link(0,i,1),link(i,0,0); 30 for(int i=1;i<=m;++i)link(n+i,n+m+1,1),link(n+m+1,n+1,0); 31 for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)if(abs(p[i]-q[j])+abs(q[j]-s)<=tim) 32 link(i,n+j,1),link(n+j,i,0); 33 while(bfs())maxflow+=dfs(0,n);//,printf("%d\n",maxflow); 34 return maxflow==n; 35 } 36 int main(){freopen("kill.in","r",stdin); 37 scanf("%d%d%d",&n,&m,&s); 38 for(int i=1;i<=n;++i)scanf("%d",&p[i]); 39 for(int j=1;j<=m;++j)scanf("%d",&q[j]); 40 int l=0,r=2000000000; 41 while(l<r-1)if(chk(l+r>>1))r=l+r>>1;else l=(l+r>>1)+1; 42 if(chk(l))printf("%d\n",l);else printf("%d\n",r); 43 }
思路积累:
- 猜测性质,发现性质,证明性质。
- 网络流乱搞二分图匹配。
- 网络流求最大权值时不能直接求,采用二分答案。
T2:beauty
贪心。
从下往上回溯时,你当然不想让子树里的点配对,让它们往上一起延伸显然会使答案更优。
但是如果子树外能配对的点的数量已经没有子树里面的多了,那么就必须在这里配对几个了。
不能理解为:如果子树里多余k就必须配对,错的。
两遍dfs。
1 #include<cstdio> 2 int n,k,a,fir[100005],l[200005],to[200005],cnt,siz[100005],r[100005];long long ans; 3 void link(int a,int b){l[++cnt]=fir[a];fir[a]=cnt;to[cnt]=b;} 4 void dfs(int p,int fa){ 5 r[p]=siz[p]; 6 for(int i=fir[p];i;i=l[i])if(to[i]!=fa)dfs(to[i],p),siz[p]+=siz[to[i]]; 7 } 8 void DFS(int p,int fa){ 9 for(int i=fir[p];i;i=l[i])if(to[i]!=fa)DFS(to[i],p),r[p]+=r[to[i]],ans+=r[to[i]]; 10 if(p==1)return; 11 while(r[p]>k*2-siz[p])r[p]-=2; 12 } 13 int main(){//freopen("beauty.in","r",stdin); 14 scanf("%d%d%d",&n,&k,&a); 15 for(int i=1,x;i<=k<<1;++i)scanf("%d",&x),siz[x]++; 16 for(int i=1,x,y;i<n;++i)scanf("%d%d",&x,&y),link(x,y),link(y,x); 17 dfs(1,0);DFS(1,0);printf("%lld\n",ans); 18 }
思路积累:
- 树上贪心:考虑链的延伸。
- 不要投机取巧,认真写式子,仔细证明。
T3:weight
数据出锅,不保证联通,所有与1号点不联通的边的答案都是0。
因为std是从1号节点开始dfs的,所以不联通的点没更新,答案都是0。
观察题目,既然题目一直在说最小生成树,那就把最小生成树弄出来吧。
发现:已经在树上的边,答案一定不比目前权值小超过1,对于非树上的边答案一定不比当前权值小。
对于非树边,只要它的权值小于最小生成树上对应两点之间的边权最大值,那么它就可以取代那条边进入最小生成树。
对于树边,它不能被取代,所以它要小于所有经过这条边的非树边的最小值。
链修改,树上两点距离,单点查询。
lct板子。或者树链剖分。或者倍增离线处理(推荐zkt的,很好打)。cbx说可以线段树合并。
思路不难,关键在实现。
因为数据有锅,所以不能用“p的父亲的重儿子是否为p”的方法判p是否为重链顶端。会炸成10分。
1 //对于不再最小生成树上的边,答案为两点之间边的最大值-1 2 //更新答案时,它只会覆盖一条链上的所有边,这条链上的所有边的答案对其权值-1取min 3 //链操作--树链剖分?lct? 4 #include<cstdio> 5 #include<algorithm> 6 using namespace std; 7 int n,m,a,fir[70005],l[200005],to[200005],siz[70005],cnt,hson[70005],tfe[70005]; 8 int ans[200005],top[70005],dep[70005],f[70005][20],mx[70005][20],v[200005]; 9 int len[70005],tcnt,lc[1000005],rc[1000005],w[1000005],rt[70005],lz[1000005]; 10 int cl[1000005],cr[1000005],tmp[70005],g[200005]; 11 void build(int &p,int l,int r){ 12 p=++tcnt;w[p]=1234567890; 13 cl[p]=l;cr[p]=r; 14 if(l==r)return; 15 build(lc[p],l,l+r>>1); 16 build(rc[p],(l+r>>1)+1,r); 17 } 18 void down(int p){//printf("%d %d %d %d %d\n",p,lc[p],rc[p],w[lc[p]],w[rc[p]]); 19 w[lc[p]]=min(w[lc[p]],lz[p]); 20 w[rc[p]]=min(w[rc[p]],lz[p]); 21 if(lz[lc[p]])lz[lc[p]]=min(lz[lc[p]],lz[p]);else lz[lc[p]]=lz[p]; 22 if(lz[rc[p]])lz[rc[p]]=min(lz[rc[p]],lz[p]);else lz[rc[p]]=lz[p]; 23 lz[p]=0;//printf("%d %d %d %d %d\n",p,lc[p],rc[p],w[lc[p]],w[rc[p]]); 24 } 25 void set(int p,int l,int r,int W){//printf("%d %d %d %d %d %d\n",p,l,r,w,cl[p],cr[p]); 26 if(l<=cl[p]&&cr[p]<=r){if(lz[p])lz[p]=min(lz[p],W);else lz[p]=W;w[p]=min(w[p],W);return;} 27 if(l<=cr[lc[p]])set(lc[p],l,r,W); 28 if(r>=cl[rc[p]])set(rc[p],l,r,W); 29 } 30 void ask(int p){//printf("%d\n",p); 31 if(cl[p]==cr[p]){tmp[cl[p]]=w[p];return;} 32 if(lz[p])down(p); 33 ask(lc[p]);ask(rc[p]); 34 } 35 struct edge{ 36 int a,b,v,num; 37 friend bool operator<(edge a,edge b){return a.v<b.v;} 38 }e[100005]; 39 void link(int a,int b,int vv,int gg){l[++cnt]=fir[a];fir[a]=cnt;to[cnt]=b;v[cnt]=vv;g[cnt]=gg;} 40 int fa[70005],in[100005]; 41 int find(int k){return fa[k]==k?k:fa[k]=find(fa[k]);} 42 void Kruscal(){ 43 sort(e+1,e+1+m); 44 for(int i=1;i<=n;++i)fa[i]=i; 45 for(int i=1;i<=m;++i)if(find(e[i].a)!=find(e[i].b)) 46 fa[fa[e[i].a]]=fa[e[i].b],in[e[i].num]=1, 47 link(e[i].a,e[i].b,e[i].v,e[i].num),link(e[i].b,e[i].a,e[i].v,e[i].num); 48 } 49 void dfs(int p,int fa){ 50 siz[p]=1;dep[p]=dep[fa]+1;f[p][0]=fa; 51 for(int i=1;i<=18;++i)f[p][i]=f[f[p][i-1]][i-1],mx[p][i]=max(mx[p][i-1],mx[f[p][i-1]][i-1]); 52 for(int i=fir[p];i;i=l[i])if(to[i]!=fa){ 53 mx[to[i]][0]=v[i]; 54 dfs(to[i],p),siz[p]+=siz[to[i]],tfe[to[i]]=g[i]; 55 if(siz[to[i]]>siz[hson[p]])hson[p]=to[i]; 56 } 57 } 58 void DFS(int p,int tp){ 59 top[p]=tp;len[tp]=dep[p]-dep[tp]+1; 60 for(int i=fir[p];i;i=l[i])if(to[i]!=f[p][0]&&to[i]!=hson[p])DFS(to[i],to[i]); 61 if(hson[p])DFS(hson[p],tp); 62 } 63 int lca(int a,int b){ 64 if(dep[a]<dep[b])swap(a,b); 65 int subdep=dep[a]-dep[b]; 66 for(int i=18;~i;--i)if(subdep&1<<i)a=f[a][i]; 67 if(a==b)return a; 68 for(int i=18;~i;--i)if(f[a][i]!=f[b][i])a=f[a][i],b=f[b][i]; 69 return f[a][0]; 70 } 71 int cal(int p,int fa){ 72 int re=0,subdep=dep[p]-dep[fa]; 73 for(int i=18;~i;--i)if(subdep&1<<i)re=max(re,mx[p][i]),p=f[p][i]; 74 return re; 75 } 76 void update(int p,int fa,int w){ 77 while(top[p]!=top[fa]){ 78 set(rt[top[p]],1,dep[p]-dep[top[p]]+1,w); 79 p=f[top[p]][0]; 80 } 81 if(p!=fa)set(rt[top[p]],dep[fa]-dep[top[p]]+2,dep[p]-dep[top[p]]+1,w); 82 } 83 void dfs_hson(int p,int ord){ 84 ans[tfe[p]]=tmp[ord]-1; 85 if(hson[p])dfs_hson(hson[p],ord+1); 86 } 87 int main(){//freopen("weight.in","r",stdin); 88 scanf("%d%d%d",&n,&m,&a); 89 for(int i=1;i<=m;++i)scanf("%d%d%d",&e[i].a,&e[i].b,&e[i].v),e[i].num=i; 90 Kruscal();dfs(1,0);DFS(1,1); 91 for(int i=1;i<=n;++i)if(len[i])build(rt[i],1,len[i]); 92 for(int i=1;i<=m;++i)if(!in[e[i].num]){ 93 int LCA=lca(e[i].a,e[i].b),DT=max(cal(e[i].a,LCA),cal(e[i].b,LCA)); 94 ans[e[i].num]=DT-1; 95 update(e[i].a,LCA,e[i].v);update(e[i].b,LCA,e[i].v); 96 } 97 for(int i=1;i<=n;++i)if(hson[f[i][0]]!=i)ask(rt[i]),dfs_hson(i,1);//for(int i=1;i<=4;++i)printf("%d\n",tmp[i]); 98 for(int i=1;i<=m;++i)if(ans[i]==1234567889)printf("-1 ");else printf("%d ",ans[i]); 99 }
思路积累:
- 链修改的各种方法:lct,熟练剖分。
- 离线。
- 最小生成树的性质。
- 判熟练剖分重儿子的方法。