[考试反思]0909NOIP模拟测试41:反典

[考试反思]0909NOIP模拟测试41:反典

[考试反思]0909NOIP模拟测试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夜莺与玫瑰

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

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

现在的问题是化简。

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

当i和j分别小于n/2和m/2时,式子的值可以化简为$ \sum\limits_{i=1}^{n/2} \sum\limits_{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递增,我们一次只处理二位数组的一行就行了。

[考试反思]0909NOIP模拟测试41:反典
 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\n",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\n",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\n",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\n",(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)查询两点距离。

[考试反思]0909NOIP模拟测试41:反典
 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\n",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\n",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\n",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\n",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\n",to[j],d[P],p1[P],p2[P]);
46             }
47         }printf("%lld\n",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即可。

[考试反思]0909NOIP模拟测试41:反典
 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",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",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\n",xx+1);
31                     bl.insert((ps){r-xx,xx});bl.insert((ps){xx-l,l});
32                     bl.erase(bl.begin());//printf("%d %d %d\n",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)
  • 代码实现细节,注意先后顺序
  • 重载比较函数的多样性

 

上一篇:树链剖分


下一篇:如何在cmd中连接数据库