[考试反思]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 }
View Code
 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 }
把freopen去掉后是个70分的暴力(二分答案,网络流判定)

思路积累:

  • 猜测性质,发现性质,证明性质。
  • 网络流乱搞二分图匹配。
  • 网络流求最大权值时不能直接求,采用二分答案。

 

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 }
View Code

思路积累:

  • 树上贪心:考虑链的延伸。
  • 不要投机取巧,认真写式子,仔细证明。

 

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 }
View Code

思路积累:

  • 链修改的各种方法:lct,熟练剖分。
  • 离线。
  • 最小生成树的性质。
  • 判熟练剖分重儿子的方法。

 

posted @ 2019-09-18 17:30  DeepinC  阅读(267)  评论(0编辑  收藏  举报