9.16题解

这套题总的来说,由于出题人的数据非常水,所以我用各种方法水了过去

T1

暴搜+剪枝

剪枝一

如果$gcd$已经变成了1那么就没有继续走下去的必要,直接用最长序列长度尝试更新答案,然后直接$break$即可

剪枝二

如果你当前的$gcd$乘上最长的区间长度,对答案也不能作出贡献,那么你已经用了最长序列长度,且越往后走$gcd$会变的越小,那么答案永远不会变优,就也没有继续下去的必要了,$break$即可

然后就水过这道题

 1 #include<iostream>
 2 #include<cstdio>
 3 #define maxn 100100
 4 #define int long long
 5 using namespace std;
 6 int n,ans;
 7 int a[maxn];
 8 int gcd(int a,int b)
 9 {
10     return b==0?a:gcd(b,a%b);
11 }
12 signed main()
13 {
14     //freopen("1.in","r",stdin);
15     scanf("%lld",&n);
16     for(int i=1;i<=n;++i)  scanf("%lld",&a[i]);
17     for(int i=1;i<=n;++i)
18     {
19         int GCD=a[i];
20         for(int j=i+1;j<=n;++j)
21         {
22             GCD=gcd(GCD,a[j]);
23             if(GCD*(n-i+1)<=ans)  break;
24             if(GCD==1)  {ans=max(ans,n-i+1);  break;}
25             ans=max(ans,GCD*(j-i+1));
26         }
27     }
28     printf("%lld\n",ans);
29     return 0;
30 }
View Code

正解直接贴了

T2

考场上用假贪心水过去了,出题人的数据造的太水了,大家以各种姿势水过了这道题

这样就已经可以过了

 1 #include<iostream>
 2 #include<cstdio>
 3 #define LL long long
 4 using namespace std;
 5 const int N=100010;
 6 const LL inf=1e12;
 7 int n,q;
 8 LL ans;
 9 LL a[N],b[N],ma1[N],ma2[N],mi1[N],mi2[N];
10 int read()
11 {
12     int s=0;char c=getchar();
13     while(c<'0'||c>'9')  c=getchar();
14     while(c>='0'&&c<='9')  {s=(s<<3)+(s<<1)+c-'0';  c=getchar();}
15     return s;
16 }
17 int main()
18 {
19     n=read();ans=inf;
20     for(int i=1;i<=n;i++)  {a[i]=read();b[i]=read();}
21     mi1[0]=mi2[0]=inf;
22     for(int i=1;i<=n;i++){
23         LL x=min(a[i],b[i]);  LL y=max(a[i],b[i]);
24         mi1[0]=min(mi1[0],x);  ma1[0]=max(ma1[0],x);
25         mi2[0]=min(mi2[0],y);  ma2[0]=max(ma2[0],y);
26     }
27     ans=(ma1[0]-mi1[0])*(ma2[0]-mi2[0]);  printf("%lld\n",ans);
28     return 0;
29 }

我的贪心在$O(n^2)$的时候是没有问题的,但是他依旧会T,我就乱搞了一下,他就A了

我的贪心思路是枚举每一个小球,找到和它差值最小的n-1个球放进一堆

正解还是粘吧

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 using namespace std;
 5 #define ll long long
 6 #define maxn 100010
 7 #define inf 2000000000000000000
 8 int n,ls1,ls2;
 9 ll hzma1[maxn],hzmi1[maxn],qzma1[maxn],qzmi1[maxn];//x一列
10 ll hzma2[maxn],hzmi2[maxn],qzma2[maxn],qzmi2[maxn];//y一列
11 ll R,r=inf,B,b=inf,ans=inf;
12 struct node{
13     ll x,y;
14 }q[maxn];
15 int main()
16 {
17     scanf("%d",&n);
18     for(int i=1;i<=n;++i) 
19     {
20         scanf("%lld%lld",&q[i].x,&q[i].y);
21         ls1=min(q[i].x,q[i].y); ls2=max(q[i].x,q[i].y);
22         q[i].x=ls1; q[i].y=ls2;
23         R=max(R,q[i].x); r=min(r,q[i].x);
24         B=max(B,q[i].y); b=min(b,q[i].y);
25     }
26     ans=(R-r)*(B-b); //cout<<ans<<endl;
27     memset(qzmi1,0x7f,sizeof(qzmi1)); memset(hzmi1,0x7f,sizeof(hzmi1));
28     memset(qzmi2,0x7f,sizeof(qzmi2)); memset(hzmi2,0x7f,sizeof(hzmi2));
29     for(int i=1;i<=n;++i) 
30     {
31         qzmi1[i]=min(qzmi1[i-1],q[i].x);
32         qzma1[i]=max(qzma1[i-1],q[i].x);
33         qzmi2[i]=min(qzmi2[i-1],q[i].y);
34         qzma2[i]=max(qzma2[i-1],q[i].y);
35     }
36     for(int i=n;i>=1;--i) 
37     {
38         hzmi1[i]=min(hzmi1[i+1],q[i].x);
39         hzma1[i]=max(hzma1[i+1],q[i].x);
40         hzmi2[i]=min(hzmi2[i+1],q[i].y);
41         hzma2[i]=max(hzma2[i+1],q[i].y);
42     }
43     for(int i=2;i<=n;++i)
44     {
45         if(qzmi2[i-1]<q[i].x) break;
46         R=max(qzma2[i-1],hzma1[i]); r=min(qzmi2[i-1],hzmi1[i]);
47         B=max(qzma1[i-1],hzma2[i]); b=min(qzmi1[i-1],hzmi2[i]);
48         ans=min(ans,(R-r)*(B-b));
49     }
50     printf("%lld",ans);
51     return 0;
52 }
放个正解代码就溜了

T3

又是线段树优化DP,最近老是见到他,还是先考虑最简单的DP,设$f[i][j][k]$代表第$i$轮,两个指针分别在$j$和$k$的最小移动距离,转移的话让两个指针都分别动一下,设$p[i]$代表第$i$轮要在的位置,那么$f[i][p[i]][k]=min(f[i][p[i]][k],f[i-1][j][k]+abs(j-p[i]))$,$f[i][j][p[i]]=min(f[i][j][p[i]],f[i-1][j][k]+abs(k-p[i]))$,数组开不到把第一维滚起来就可以了,然而,这种$O(n^3)$的非常不优秀算法,依旧会得到$T0$的好成绩,考虑一下,除了滚动之外,我们是不是可以省掉后面两维中的某一维,我们会发现,除了初始状态之外,剩余所有状态,都有一个指针在$p[i]$的位置,所以有一维是和$i$直接相关的,我们可以省掉他,现在dp数组就变成了$f[i][j]$,代表第$i$轮,一个指针在$j$的位置上,另一个指针在$p[i]$的位置上的最小移动距离,转移的话一个是在上一轮在$p[i-1]$的位置上挪过来,另一种是不在$p[i-1]$的位置上挪过来,如果在$p[i-1]$的位置上挪过来那么$f[i][j]=min(f[i][j],f[i-1][j]+abs(p[i-1]-j))$,如果不在$p[i-1]$的位置上挪过来,那么$f[i][p[i-1]]=min(f[i][p[i-1]],f[i-1][j]+abs(j-p[i]))$,这样的话,转移就变成了$O(n^2)$的dp,依旧过不太去,考虑一下他的转移可以对应线段树中的区间加,区间最小值查询,单点修改,那么我们就把这个dp摁到线段树上去,由于我们有个$abs$的存在,所以线段树维护最小值,同时需要$f[i][j]-j$和$f[i][j]+j$,就都维护了,把$p[i]$和$j$的大小分情况,去线段树查询区间最小值即可

  1 #include<iostream>
  2 #include<cstring>
  3 #include<cstdio>
  4 #include<cmath>
  5 #define maxn 100100
  6 #define int long long
  7 #define inf 2000000000000000000
  8 using namespace std;
  9 struct node{
 10     int zuo,you,w,lan,zxa/*+j*/,zxn/*-j*/;
 11 }a[maxn<<2];
 12 int n,q,A,B,ans=inf;
 13 int p[maxn];
 14 void up(int fa)
 15 {
 16     a[fa].w=min(a[2*fa].w,a[2*fa+1].w);
 17     a[fa].zxa=min(a[2*fa].zxa,a[2*fa+1].zxa);
 18     a[fa].zxn=min(a[2*fa].zxn,a[2*fa+1].zxn);
 19 }
 20 void down(int fa)
 21 {
 22     a[2*fa].w+=a[fa].lan;  a[2*fa+1].w+=a[fa].lan;
 23     a[2*fa].zxa+=a[fa].lan;  a[2*fa+1].zxa+=a[fa].lan;
 24     a[2*fa].zxn+=a[fa].lan;  a[2*fa+1].zxn+=a[fa].lan;
 25     a[2*fa].lan+=a[fa].lan;  a[2*fa+1].lan+=a[fa].lan;
 26     a[fa].lan=0;
 27 }
 28 void build(int fa,int l,int r)
 29 {
 30     a[fa].zuo=l;  a[fa].you=r;  a[fa].w=inf;  a[fa].zxa=inf;  a[fa].zxn=inf;
 31     if(l==r)
 32     {
 33         if(l==A)  {int ls=abs(p[1]-B);  a[fa].w=min(a[fa].w,ls);}
 34         if(l==B)  {int ls=abs(p[1]-A);  a[fa].w=min(a[fa].w,ls);}
 35         a[fa].zxa=a[fa].w+a[fa].zuo;  a[fa].zxn=a[fa].w-a[fa].zuo;
 36         return ;
 37     }
 38     int mid=(l+r)>>1;
 39     build(2*fa,l,mid);  build(2*fa+1,mid+1,r);  up(fa);
 40 }
 41 void add(int fa,int l,int r,int w)
 42 {
 43     if(a[fa].zuo>=l&&a[fa].you<=r)
 44     {
 45         a[fa].w+=w;  a[fa].zxa+=w;  a[fa].zxn+=w;  a[fa].lan+=w;
 46         return ;
 47     }
 48     if(a[fa].lan)  down(fa);
 49     int mid=(a[fa].zuo+a[fa].you)>>1;
 50     if(l<=mid)  add(2*fa,l,r,w);
 51     if(r>mid)  add(2*fa+1,l,r,w);
 52     up(fa);
 53 }
 54 void change(int fa,int pos,int w)
 55 {
 56     if(a[fa].zuo==a[fa].you)
 57     {
 58         a[fa].w=w;  a[fa].zxa=w+a[fa].zuo;  a[fa].zxn=w-a[fa].zuo;
 59         return ;
 60     }
 61     if(a[fa].lan)  down(fa);
 62     int mid=(a[fa].zuo+a[fa].you)>>1;
 63     if(pos<=mid)  change(2*fa,pos,w);
 64     else  change(2*fa+1,pos,w);
 65     up(fa);
 66 }
 67 int query1(int fa,int l,int r)//-j
 68 {
 69     if(a[fa].zuo>=l&&a[fa].you<=r)  return a[fa].zxn;
 70     if(a[fa].lan)  down(fa);
 71     int mid=(a[fa].zuo+a[fa].you)>>1,minn=inf;
 72     if(l<=mid)  minn=min(minn,query1(2*fa,l,r));
 73     if(r>mid)  minn=min(minn,query1(2*fa+1,l,r));
 74     return minn;
 75 }
 76 int query2(int fa,int l,int r)//+j
 77 {
 78     if(a[fa].zuo>=l&&a[fa].you<=r)  return a[fa].zxa;
 79     down(fa);
 80     int mid=(a[fa].zuo+a[fa].you)>>1,minn=inf;
 81     if(l<=mid)  minn=min(minn,query2(2*fa,l,r));
 82     if(r>mid)  minn=min(minn,query2(2*fa+1,l,r));
 83     return minn;
 84 }
 85 void upp(int fa)
 86 {
 87     if(a[fa].zuo==a[fa].you)  return ;
 88     down(fa);  upp(2*fa);  upp(2*fa+1);  up(fa);
 89 }
 90 signed main()
 91 {
 92     //freopen("3.in","r",stdin);
 93     //freopen("W.out","w",stdout);
 94     scanf("%lld%lld%lld%lld",&n,&q,&A,&B);
 95     for(int i=1;i<=q;++i)  scanf("%lld",&p[i]);
 96     build(1,1,n);
 97     for(int i=2;i<=q;++i)
 98     {
 99         int minn=query1(1,1,p[i])+p[i];
100         minn=min(minn,query2(1,p[i]+1,n)-p[i]);
101         add(1,1,n,abs(p[i]-p[i-1]));  change(1,p[i-1],minn);
102     }
103     upp(1);  printf("%lld\n",a[1].w);
104     return 0;
105 }
View Code

 

posted @ 2019-10-09 21:04  hzoi_X&R  阅读(178)  评论(6编辑  收藏  举报