[考试反思]0916csp-s模拟测试44:可笑
出现了有史以来第一个3首杀AK啊。。。然而跟我没有丝毫关系
(曾经还是有一次考试差点就有那么一点关系的。。。)
然而反正我考得很水就是了。不是很垃圾,而是很水。
这套题是真的水。。。
T1不会证复杂度,但是A掉了,数据很难造所以对拍基本上是白打了。。。
复杂度是对的。数据很水。
T2的话想了挺久,想到要分两种情况讨论,一种简单贪心即可,另一种比较复杂。
考场上没有发现单调性于是复杂度n2了。
但是数据水的过分只要打出来第一种就行了,极其荒谬,样例都不过就能A。
当然我两种情况都打了,复杂度也没被卡。
%%%kx只打了复杂的第二种情况,但是垃圾测试点之留下了他爆搜的10分,RP++;
T3暴力dp对了但是没读入。不知怎么的反正过样例了
我自己都要笑死了。。。。
如果读入了就是230了rank2呢。。。哪里有什么如果
一定要手模样例,尤其在出题人只给了一个小样例的时候
T1:D
比较简单。做成了STL题。
用vector维护每一个因数连续出现的最早位置。用map维护每一个因数在vector里的下标。
打麻烦了。
复杂度O(n log2n)
1 #include<cstdio> 2 #include<vector> 3 #include<map> 4 using namespace std; 5 vector<int>v[2],p[2]; 6 map<int,int>m; 7 int gcd(int a,int b){while(b)b^=a^=b^=a%=b;return a;} 8 int n,a[100005];long long ans; 9 int main(){//freopen("1.in","r",stdin);freopen("co.out","w",stdout); 10 scanf("%d",&n); 11 for(int i=1;i<=n;++i)scanf("%d",&a[i]); 12 v[1].push_back(a[1]);p[1].push_back(1);ans=a[1]; 13 for(int i=2;i<=n;++i){ 14 for(int j=0;j<v[i&1^1].size();++j){ 15 int G=gcd(a[i],v[i&1^1][j]),M=m[G],P=p[i&1^1][j]; 16 if(M)P=p[i&1][M-1]=min(p[i&1][M-1],P); 17 else v[i&1].push_back(G),p[i&1].push_back(P),m[G]=v[i&1].size(); 18 ans=max(ans,1ll*G*(i-P+1)); 19 } 20 ans=max(ans,1ll*a[i]); 21 if(m.find(a[i])==m.end())v[i&1].push_back(a[i]),p[i&1].push_back(i); 22 m.clear(); 23 v[i&1^1].clear();p[i&1^1].clear(); 24 } 25 printf("%lld\n",ans); 26 }
思路积累:
- 相邻项取gcd以取代枚举所有因子
- 只保存较大的因子,让较大的因子在后面需要的时候再分解成小因子
T2:E
两种情况。所有球里面的最大值$T_{max}$和最小值$T_{min}$要么是不同的颜色,要么在一起。
对于第一种情况式子是$(T_{max} - R_{min})*(B_{max} - T_{min})$
那么就把大的求放进左边,小的放进右边。
第二种情况是$(T_{max} - T_{min})*(R_{max} - R_{min})$
这时候左边就相当于垃圾桶,所有不要的值都往里面扔就是了。
目标就是在每一对球里选一个,使选出的球极差尽量小。
枚举$R_{min}$在考虑我们能否进行决策。
那就是如果一对球里小的那一个大于等于$R_{min}$那么就选小的,否则就选大的。
如果大的球也比$R_{min}$小那么这个就不合法了,更大的$R_{min}$也就不合法了。跳出。
这是O(n2)的。
但是如果我们从小到大枚举$R_{min}$的话,可以发现决策的变化就是把几个小球变成了大球。
那么每对球至多变化一次,单调指针扫的话总复杂度O(n)。
粘考场的O(n2)代码了。
1 #include<cstdio> 2 #include<algorithm> 3 #include<ctime> 4 using namespace std; 5 struct ps{ 6 int w,opt; 7 friend bool operator<(ps a,ps b){ 8 return a.w<b.w||(a.w==b.w&&a.opt>b.opt); 9 } 10 }p[200005]; 11 int l[100005],r[100005],n,mxmx,mxmn=1e9,mnmx,mnmn=1e9;long long ans; 12 int main(){ 13 scanf("%d",&n); 14 for(int i=1;i<=n;++i)scanf("%d%d",&l[i],&r[i]); 15 for(int i=1;i<=n;++i)if(l[i]>r[i])l[i]^=r[i]^=l[i]^=r[i]; 16 for(int i=1;i<=n;++i)mxmx=max(mxmx,r[i]),mxmn=min(mxmn,r[i]),mnmx=max(mnmx,l[i]),mnmn=min(mnmn,l[i]); 17 ans=1ll*(mxmx-mxmn)*(mnmx-mnmn); 18 for(int i=1;i<=n;++i)p[i].w=l[i],p[n+i].w=r[i],p[i].opt=1; 19 sort(p+1,p+n+n+1);p[0].opt=1; 20 for(int j=1;p[j-1].opt&&clock()<1990000;++j){ 21 int mnn=p[j].w,mxx=0;//printf("%d\n",mnn); 22 for(int i=1;i<=n;++i)if(l[i]>=mnn)mxx=max(mxx,l[i]);else mxx=max(mxx,r[i]); 23 ans=min(ans,1ll*(mxmx-mnmn)*(mxx-mnn)); 24 } 25 printf("%lld\n",ans); 26 }
思路积累:
- 贪心。
- 单调指针:决策的连续性。
- 分类讨论。
T3:F
首先要想到O(n2)的dp。
刚开始是想的三维,存第几轮时两个指针分别的位置。
然而其实完成一个指令后其中一个指针一定在目标处,记录另一个指针即可。
然后dp式子可以看出是区间加,单点修改,区间取min。
第一个式子直接区间加,第二个式子就是单点取min。
因为带绝对值所以拆开,左边的问f-j,右边的问f+j,两者取min后单点修改。
线段树。过程量爆int了。
记得读入。
1 #include<cstdio> 2 #include<iostream> 3 #include<cmath> 4 using namespace std; 5 #define int long long 6 int w[800005][3],cl[800005],cr[800005],n,Q,q[100005],b,lz[800005]; 7 void build(int p,int l,int r){ 8 cl[p]=l;cr[p]=r; 9 if(l==r){ 10 if(l==b)w[p][0]=0,w[p][1]=-l,w[p][2]=l; 11 else w[p][0]=w[p][1]=w[p][2]=1e16; 12 return; 13 } 14 build(p<<1,l,l+r>>1);build(p<<1|1,(l+r>>1)+1,r); 15 for(int opt=0;opt<=2;++opt)w[p][opt]=min(w[p<<1][opt],w[p<<1|1][opt]); 16 } 17 void down(int p){ 18 for(int opt=0;opt<=2;++opt)w[p<<1][opt]+=lz[p],w[p<<1|1][opt]+=lz[p]; 19 lz[p<<1]+=lz[p];lz[p<<1|1]+=lz[p]; 20 lz[p]=0; 21 } 22 void set(int p,int pos,int W){ 23 if(cl[p]==cr[p]){ 24 if(W<w[p][0])w[p][0]=W,w[p][1]=W-pos,w[p][2]=W+pos; 25 return; 26 } 27 if(lz[p])down(p); 28 if(pos<=cr[p<<1])set(p<<1,pos,W); 29 else set(p<<1|1,pos,W); 30 for(int opt=0;opt<=2;++opt)w[p][opt]=min(w[p<<1][opt],w[p<<1|1][opt]); 31 } 32 void add(int p,int l,int r,int W){ 33 if(l<=cl[p]&&cr[p]<=r){ 34 lz[p]+=W; 35 for(int opt=0;opt<=2;++opt)w[p][opt]+=W; 36 return; 37 } 38 if(lz[p])down(p); 39 if(l<=cr[p<<1])add(p<<1,l,r,W); 40 if(r>=cl[p<<1|1])add(p<<1|1,l,r,W); 41 for(int opt=0;opt<=2;++opt)w[p][opt]=min(w[p<<1][opt],w[p<<1|1][opt]); 42 } 43 int ask(int p,int l,int r,int opt){ 44 if(l<=cl[p]&&cr[p]<=r)return w[p][opt]; 45 if(lz[p])down(p); 46 return min(l<=cr[p<<1]?ask(p<<1,l,r,opt):1e16,r>=cl[p<<1|1]?ask(p<<1|1,l,r,opt):1e16); 47 } 48 main(){ 49 scanf("%lld%lld%lld%lld",&n,&Q,&q[0],&b); 50 build(1,1,n); 51 for(int i=1;i<=Q;++i)scanf("%lld",&q[i]); 52 for(int i=1;i<=Q;++i){ 53 int mn1=ask(1,1,q[i],1),mn2=ask(1,q[i],n,2); 54 add(1,1,n,abs(q[i]-q[i-1])); 55 set(1,q[i-1],min(mn1+q[i],mn2-q[i])); 56 } 57 printf("%lld\n",ask(1,1,n,0)); 58 }
思路积累:
- 线段树优化dp:把操作更新抽象为区间/单点操作,适用于第二维存值域(到现在靠自己一个都没做出来!!!)
- 拆绝对值,左右分别讨论。