zoukankan      html  css  js  c++  java
  • [考试反思]0909csp-s模拟测试41:反典

    说在前面:我是反面典型!!!不要学我!!!

    说在前面:向rank1某脸学习,不管是什么题都在考试反思后面稍微写一下题解。

    这次是真的真的运气好。。。

    这次知识点上还可以,但是答题策略出了问题。。。

    幸亏平时攒的rp生效了(前一天比较耐心的教不少人题来着,与某脸形成对比rp++)(然而可能也并不耐心)

    考试过程:

    考前,大脸说他可能不是第一个AK csp-s赛制的人,但是应该是第一个AC的。

    于是我的目标就变成了抢第一个AC:我要快速切掉最简单的题!!!

    考前flag必倒。。。

    于是反正第一步就是先找到最简单的题吧。。。

    过了一边三道题:T1显然需要找规律并不是很快能切掉,T2数据结构去死吧不会

    等等,T3这是啥?大力模拟?STL set可以水?

    发现自己hack不掉,然后就开始码。

    好写好调。(其实都没怎么调,过编译就过样例)

    刚好这题没有大一点的样例,我就直接交了。

    然而心急了。细节打错了!

    不要给自己凭空施压!

    从时间上看的确是第一个,但不是第一个AC。。。

    考后5分钟乱改就A了。

    然后相较于T2当然我宁愿滚去推式子于是T2暴力都没有打。

    然后就去推式子了。然后就差不多死了。(详见下方题解)

    暴力很好弄啊。。。但是用了一个小时

    但是我的暴力始终过不了小样例里的某一个询问,其实是我手抄样例时抄错了。

    我还以为我打错了,心态略崩。抱着骗分的想法继续了。

    然后发现会MLE,优化,又用了一个小时。。。

    这还是比某孩子tdcp直接MLE0好一些的。

    不要忘了还有MLE这种错。

    于是我还有20分钟,不抛弃不放弃去T2打暴力。

    14分钟完成,过不了样例结果发现和我手模结果一致。。。我手模都不对。。。?!

    又看错题了!!!

    做题之前不管时间多么紧迫一定要好好看题再手模一个样例!!!不要白费时间!!!

    然后就结束了,平时一定会炸一道题,我就交了两个如果炸掉一个的话就只剩100分/80分了

    但是rp爆发没有爆炸,拿到了100+空白+80=180。

    然后教练说:吴迪是一个反面示范。

    我错了了了了了了了。。。。。

    一定要打暴力!!!!

    题解区:

    T1:夜莺与玫瑰

    我的式子是$ sumlimits_{i=1}^{n-1} sumlimits_{j=1}^{m-1} [gcd(i,j)==1] imes (min(i,n-i) imes (m-j) + min(j,m-j) imes (n-i) - min(i,n-i) imes min(j,m-j)) $

    很长,很丑,但是有具体含义,我给Alpaca解释过了但是实在太复杂,对实际含义感兴趣的可以来找我。

    现在的问题是化简。

    我们可以发现最不好搞的两项就是min的项,但是只要分类讨论i和n-i的关系以及j同理,那么就可求了。

    当i和j分别小于n/2和m/2时,式子的值可以化简为$ sumlimits_{i=1}^{n/2} sumlimits_{j=1}^{m/2} [gcd(i,j)==1] n*j+m*i-i*j*3 $

    在其他情况下,同理可以化简为只含有ij,im,jn,ij的项

    那么我们可以预处理ij,i,j,1在gcd(i,j)==1时的前缀和,4个二维数组干上再O(1)计算即可

    发现会MLE。

    那么我们就可以离线所有询问,使询问的n递增,我们一次只处理二位数组的一行就行了。

     1 //for(int i=1;i<=n-1;++i)for(int j=1;j<=m-1;++j)if(gcd(i,j)==1)
     2 //    ans+=min(n-i,i)*(m-j)+(n-i)*min(m-j,j)-min(n-i,i)*min(m-j,j);
     3 #include<cstdio>
     4 #include<algorithm>
     5 using namespace std;
     6 const long long mod=1<<30;
     7 long long sig_1[4005],sig_i[4005],sig_j[4005],sig_ij[4005];
     8 long long si_1[4005],si_i[4005],si_j[4005],si_ij[4005];
     9 long long s_1,s_i,s_j,s_ij;
    10 int gcd(int a,int b){return b?gcd(b,a%b):a;}
    11 struct ps{int m,n,ord,ans;}p[10005];
    12 bool com_n(ps a,ps b){return a.n<b.n;}
    13 bool com_ord(ps a,ps b){return a.ord<b.ord;}
    14 void Mod(long long &p){if(p>=mod)p-=mod;}
    15 int main(){
    16     int T,p1=0,p2=0;
    17     scanf("%d",&T);
    18     for(int i=1;i<=T;++i)scanf("%d%d",&p[i].n,&p[i].m),p[i].ord=i;
    19     sort(p+1,p+1+T,com_n);
    20     for(int i=1;i<=T;++i){
    21         int n=p[i].n,m=p[i].m;
    22         while(p1<n/2){
    23             p1++;
    24             for(int j=1;j<=2000;++j){
    25                 if(gcd(p1,j)==1)s_1++,Mod(s_i+=p1),Mod(s_j+=j),(s_ij+=p1*j)%=mod;
    26                 Mod(sig_1[j]+=s_1),Mod(sig_i[j]+=s_i),
    27                 Mod(sig_j[j]+=s_j),Mod(sig_ij[j]+=s_ij);
    28             }
    29             s_1=s_i=s_j=s_ij=0;
    30         }
    31         while(p2<n-1){
    32             p2++;
    33             for(int j=1;j<=4000;++j){
    34                 if(gcd(p2,j)==1)s_1++,Mod(s_i+=p2),Mod(s_j+=j),(s_ij+=p2*j)%=mod;//if(j<=2)printf("--%lld
    ",s_1);
    35                 Mod(si_1[j]+=s_1),Mod(si_i[j]+=s_i),
    36                 Mod(si_j[j]+=s_j),Mod(si_ij[j]+=s_ij);//if(j<=2)printf("%lld
    ",si_1[j]);
    37             }
    38             s_1=s_i=s_j=s_ij=0;
    39         }
    40         p[i].ans=(mod+(
    41             (-n*m*sig_1[m/2]-4*sig_ij[m/2]+2*sig_i[m/2]*m+2*sig_j[m/2]*n)
    42             +
    43             (n*m*si_1[m-1]+si_ij[m-1]-si_i[m-1]*m-si_j[m-1]*n)
    44         )%mod)%mod;//printf("%lld %lld %lld %lld
    ",si_1[m-1],si_ij[m-1],si_i[m-1],si_j[m-1]);
    45     }
    46     sort(p+1,p+1+T,com_ord);
    47     for(int i=1;i<=T;++i)printf("%lld
    ",(p[i].ans*2+p[i].n+p[i].m)%mod);
    48 }
    View Code

    记录思想(包括其它大神用的):

    • 离线
    • 记忆化gcd(去掉log)
    • 反演(带根号回答询问)
    • 拆式子,去min,max,保留不同的0,1,2次项求前缀和
    • 卡空间,运行内存实际很小,2×4000×4000在128M下可过
    • 询问排序递增,单调指针硬扫

    T2:影子

    考场上没打,不多说

    前置知识:树的直径。

    树上任意一点开始dfs,距离最远的点一定是直径的两个端点之一。

    接下来按照点权排序,从大到小解锁每一个点。

    然后我们用并查集维护当前树的直径以及端点,每次合并AB时分别讨论:

    1.直径就是A里的直径

    2.就是B里的

    3-6.是从A的端点里选一个,走到目前枚举的边,再走到B的一个端点

    取最优决策,用直径×点权更新ans。

    因为前两种情况里直径一定被更大或相等的点权枚举过,所以用较小的点不会使ans变大,故决策最优。

    预处理,倍增求LCA,树上前缀和,实现O(log)查询两点距离。

     1 #include<cstdio>
     2 #include<iostream>
     3 #include<algorithm>
     4 using namespace std;
     5 #define int long long
     6 int t,n,fir[100005],l[200005],to[200005],cnt,f[100005],al[100005],p1[100005],p2[100005];
     7 int ans,v[200005],d[100005],F[20][100005],dep[100005],dt[100005];
     8 struct ps{int p,v;friend bool operator<(ps a,ps b){return a.v>b.v;}}p[100005];
     9 void link(int a,int b,int vv){l[++cnt]=fir[a];fir[a]=cnt;to[cnt]=b;v[cnt]=vv;}
    10 int find(int k){return f[k]==k?k:f[k]=find(f[k]);}
    11 void dfs(int p,int fa){
    12     F[0][p]=fa;dep[p]=dep[fa]+1;
    13     for(int i=1;i<=19;++i)F[i][p]=F[i-1][F[i-1][p]];
    14     for(int i=fir[p];i;i=l[i])if(to[i]!=fa)dt[to[i]]=dt[p]+v[i],dfs(to[i],p);
    15 }
    16 int DT(int a,int b){//printf("DT:%lld %lld
    ",a,b);
    17     int A=a,B=b,subdep=dep[a]-dep[b];
    18     if(subdep<0)subdep=-subdep,a^=b^=a^=b,A^=B^=A^=B;
    19     for(int i=0;i<=19;++i)if(subdep&1<<i)a=F[i][a];
    20     if(a==b)return dt[A]-dt[B];
    21     for(int i=19;~i;--i)if(F[i][a]!=F[i][b])a=F[i][a],b=F[i][b];
    22     a=F[0][a];
    23     return dt[A]+dt[B]-dt[a]-dt[a];
    24 }
    25 main(){//freopen("b.in","r",stdin);
    26     //freopen("x.in","r",stdin);//freopen("my.out","w",stdout);
    27     scanf("%lld",&t);
    28     while(t--){
    29         scanf("%lld",&n);
    30         for(int i=1;i<=n;++i)f[i]=p1[i]=p2[i]=i;
    31         for(int i=1;i<=n;++i)scanf("%lld",&p[i].v),p[i].p=i;
    32         sort(p+1,p+1+n);
    33         for(int i=1,a,b,vv;i<n;++i)scanf("%lld%lld%lld",&a,&b,&vv),link(a,b,vv),link(b,a,vv);
    34         dfs(1,0);
    35         for(int i=1;i<=n;++i){
    36             int P=p[i].p;al[P]=1;//printf("Unlocked : %lld
    ",P);
    37             for(int j=fir[P];j;j=l[j])if(al[to[j]]){
    38                 int F=find(to[j]);f[F]=P;
    39                 int d1=DT(P,p1[P]),d2=DT(P,p2[P]),totd,c1,c2;
    40                 if(d1>d2)totd=d1,c1=p1[P];else totd=d2,c1=p2[P];//printf("%lld %lld %lld %lld
    ",d1,d2,p1[P],p2[P]);
    41                 d1=DT(to[j],p1[F]),d2=DT(to[j],p2[F]);
    42                 if(d1>d2)totd+=d1,c2=p1[F];else totd+=d2,c2=p2[F];//printf("%lld %lld %lld %lld
    ",d1,d2,p1[F],p2[F]);
    43                 if(totd+v[j]>d[P])d[P]=totd+v[j],p1[P]=c1,p2[P]=c2;//,puts("UPD");
    44                 if(d[F]>d[P])d[P]=d[F],p1[P]=p1[F],p2[P]=p2[F];
    45                 ans=max(ans,d[P]*p[i].v);//printf("%lld %lld %lld %lld
    ",to[j],d[P],p1[P],p2[P]);
    46             }
    47         }printf("%lld
    ",ans);
    48         for(int i=1;i<=n;++i)fir[i]=d[i]=al[i]=0;
    49         cnt=ans=0;
    50     }
    51 }
    View Code

    思路积累:

    • 并查集维护联通块
    • 树的直径性质
    • 点分治做法(没打,但是简单粗暴,难打好想)
    • 贪心,按顺序解锁点
    • 预处理做到O(log)求树上点对距离{树上差分+倍增LCA}
    • 要会打暴力

    T3:玫瑰花精

    set大力模拟即可。

    维护两个set,一个存两个花精之间的间隔,另一个存位置。

    不断按照题意insert,erase即可。

     1 #include<cstdio>
     2 #include<set>
     3 #include<vector>
     4 using namespace std;
     5 struct ps{
     6     int dt,sp;
     7     friend bool operator<(ps a,ps b){
     8         return a.dt/2>b.dt/2||(a.dt/2==b.dt/2&&a.sp<b.sp);
     9     }
    10 };
    11 set<ps>bl;set<int>p;
    12 int n,m,o,x,ip[1000005];
    13 int main(){//freopen("c.in","r",stdin);
    14     scanf("%d%d",&n,&m);
    15     while(m--){
    16         scanf("%d%d",&o,&x);
    17         if(o==1){
    18             if(p.size()==0)p.insert(0),ip[x]=0,puts("1");
    19             else if(p.size()==1){
    20                 int xx=*p.begin();
    21                 if(xx>=n>>1)p.insert(0),ip[x]=0,puts("1"),bl.insert((ps){xx,0});
    22                 else p.insert(n-1),ip[x]=n-1,printf("%d
    ",n),bl.insert((ps){n-xx-1,xx});
    23             }
    24             else{
    25                 int p1=*p.begin(),p2=n-(*(--p.end()))-1,p3=(*bl.begin()).dt/2,p3p=(*bl.begin()).sp;
    26                 if(p1>=p2&&p1>=p3)p.insert(0),ip[x]=0,puts("1"),bl.insert((ps){p1,0});
    27                 else if(p2>p3)p.insert(n-1),ip[x]=n-1,printf("%d
    ",n),bl.insert((ps){p2,n-p2-1});
    28                 else{
    29                     int l=p3p,r=(*bl.begin()).dt+p3p,xx=l+r>>1;
    30                     p.insert(xx);ip[x]=xx;printf("%d
    ",xx+1);
    31                     bl.insert((ps){r-xx,xx});bl.insert((ps){xx-l,l});
    32                     bl.erase(bl.begin());//printf("%d %d %d
    ",l,r,xx);
    33                 }
    34             }
    35         }else{
    36             if(p.size()<=2)p.erase(ip[x]),bl.clear();
    37             else{
    38                 if(ip[x]==(*p.begin()))bl.erase((ps){(*p.upper_bound(ip[x]))-ip[x],ip[x]});
    39                 else if(ip[x]>=(*(--p.end())))bl.erase((ps){ip[x]-(*(--p.end())),(*(--p.end()))});
    40                 else{
    41                     int lp=*(--(p.lower_bound(ip[x]))),rp=*(p.upper_bound(ip[x]));
    42                     bl.erase((ps){rp-ip[x],ip[x]});bl.erase((ps){ip[x]-lp,lp});
    43                     bl.insert((ps){rp-lp,lp});
    44                 }
    45                 p.erase(ip[x]);
    46             }
    47         }
    48     }
    49 }
    View Code

    思路积累:

    • STL的熟练应用
    • 线段树维护最长区间(同《旅馆Hotel》,我还没有A)
    • 代码实现细节,注意先后顺序
    • 重载比较函数的多样性
  • 相关阅读:
    pipelinewise 学习二 创建一个简单的pipeline
    pipelinewise 学习一 docker方式安装
    Supercharging your ETL with Airflow and Singer
    ubuntu中使用 alien安装rpm包
    PipelineWise illustrates the power of Singer
    pipelinewise 基于singer 指南的的数据pipeline 工具
    关于singer elt 的几篇很不错的文章
    npkill 一个方便的npm 包清理工具
    kuma docker-compose 环境试用
    kuma 学习四 策略
  • 原文地址:https://www.cnblogs.com/hzoi-DeepinC/p/11498807.html
Copyright © 2011-2022 走看看