zoukankan      html  css  js  c++  java
  • 模拟14 题解

    T1[A. 旋转子段]

    20%算法

    枚举旋转起始点,再枚举旋转长度,得到旋转区间,再扫一边统计答案,

    60%算法

    考虑节省统计答案的复杂度,预处理出来每个节点的初始固定点个数

    枚举旋转的中心,(可以是原来的点或某两个点中间的轴),再从中心点向两边扩展区间,出现了新的固定点时cnt++

    对于区间外的前缀和O(1)查询

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<cmath>
     5 #include<algorithm>
     6 #define R register
     7 using namespace std;
     8 const int maxn=100005;
     9 inline int read()
    10 {
    11     int f=1,x=0;char ch=getchar();
    12     while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
    13     while(ch<='9'&&ch>='0'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
    14     return f*x;
    15 }
    16 int n,a[maxn],t[maxn],per[maxn];
    17 int main()
    18 {
    19     n=read();
    20     for(R int i=1;i<=n;++i){
    21         a[i]=read();
    22         per[i]=per[i-1];
    23         if(i==a[i])
    24             per[i]++;
    25     }
    26     R int ans=per[n];
    27     for(int i=1;i<=2*n-1;++i)
    28     {
    29         int sum=0,x,cnt=0;
    30         if(i&1)//奇数是指原点
    31         {            
    32             x=(i+1)>>1;
    33             if(a[x]==x)cnt++;
    34             for(int l=x-1,r=x+1;l>=1&&r<=n;--l,++r)
    35             {
    36                 sum=per[l-1]+per[n]-per[r];
    37                 if(a[l]==r)cnt++;
    38                 if(a[r]==l)cnt++;
    39                 sum+=cnt;
    40                 ans=max(ans,sum);
    41             }
    42         }
    43         else
    44         {
    45             x=i>>1;
    46             for(int l=x,r=x+1;l>=1&&r<=n;--l,++r)
    47             {
    48                 sum=per[l-1]+per[n]-per[r];
    49                 if(a[l]==r)cnt++;
    50                 if(a[r]==l)cnt++;
    51                 sum+=cnt;
    52                 ans=max(ans,sum);    
    53             }
    54         }
    55     }
    56     printf("%d
    ",ans);
    57 }
    View Code

    100%算法

    假设[l,r]为最优解区间,则必定有l==a[r]或a[l]==r(否则一定可以将区间扩展或缩小)

     那么将每组点对(i,a[i])按照l为较小值,r为较大值,放入下标为i+a[i]的vector中

    此时的每个vector里存的点都是有相同的轴的点对,将vector中的点按照大区间包含小区间的方式排序,从小区间开始向外扩展,cnt++,记录枚举到当前区间的里面固定点个数

    区间外仍然使用前缀和

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<cmath>
     5 #include<algorithm>
     6 #include<vector>
     7 #define R register
     8 using namespace std;
     9 const int maxn=500005;
    10 inline int read()
    11 {
    12     int f=1,x=0;char ch=getchar();
    13     while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
    14     while(ch<='9'&&ch>='0'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
    15     return f*x;
    16 }
    17 int n,a[maxn],per[maxn];
    18 struct node{
    19     int l,r;
    20 };
    21 bool cmp(node x,node y){return x.l>y.l;}
    22 vector<node>v[2*maxn];
    23 int main()
    24 {
    25     //freopen("data","r",stdin);
    26     //freopen("1.out","w",stdout);
    27     n=read();
    28     for(R int i=1;i<=n;++i){
    29         a[i]=read();
    30         node tt;
    31         tt.l=min(i,a[i]),tt.r=max(i,a[i]);
    32         v[i+a[i]].push_back(tt);
    33         per[i]=per[i-1];
    34         if(i==a[i])
    35             per[i]++;
    36     }
    37     R int ans=per[n];
    38     for(int i=1;i<=2*n-1;++i)
    39     {
    40         sort(v[i].begin(),v[i].end(),cmp);
    41         int cnt=0;
    42         for(int k=0;k<v[i].size();++k)
    43         {
    44             int ll=v[i][k].l,rr=v[i][k].r;
    45             int sum=per[ll-1]+per[n]-per[rr];
    46             cnt++;
    47             sum+=cnt;
    48             ans=max(ans,sum);
    49         }
    50     }
    51     printf("%d
    ",ans);
    52 }
    View Code

    T2[B. 走格子]

    把网格图建边成图对于每个非墙节点,连向与之相邻的四个格子(特判是不是墙),再连向四个方向能到的最近的墙的前一个格子,

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<cmath>
     5 #include<algorithm>
     6 #include<queue>
     7 #include<vector>
     8 #define R register
     9 #define INF 1061109567
    10 using namespace std;
    11 const int maxn=250005;
    12 int n,m,a[505][505];
    13 int d[maxn],v[maxn];
    14 vector<int>r[505],c[505];
    15 int st,en;
    16 struct node{
    17     int u,w,v,nxt;
    18 }e[16*maxn];int h[maxn],nu;
    19 void add(int x,int y,int z)
    20 {
    21     e[++nu].u=x;
    22     e[nu].v=y;
    23     e[nu].nxt=h[x];
    24     e[nu].w=z;
    25     h[x]=nu;
    26 }
    27 int cal(int x,int y){return (x-1)*m+y;}
    28 void spfa()
    29 {
    30     queue<int>q;
    31     memset(d,0x3f,sizeof d);
    32     d[st]=0,v[st]=1;
    33     q.push(st);
    34     while(q.size())
    35     {
    36         int x=q.front();
    37         q.pop(),v[x]=0;
    38         for(int i=h[x];i;i=e[i].nxt)
    39         {
    40             int y=e[i].v;
    41             if(d[y]>d[x]+e[i].w){
    42                 d[y]=d[x]+e[i].w;
    43                 if(!v[y])q.push(y),v[y]=1;
    44             }
    45         }
    46     }
    47 }
    48 int main()
    49 {
    50     scanf("%d%d",&n,&m);
    51     for(int i=1;i<=n;i++)
    52     {
    53         char ci[505];
    54         scanf("%s",ci+1);
    55         for(int j=1;j<=m;j++)
    56         {
    57             if(ci[j]=='#')a[i][j]=1,r[i].push_back(j),c[j].push_back(i);
    58             if(ci[j]=='C')st=cal(i,j);
    59             if(ci[j]=='F')en=cal(i,j);
    60         }
    61     }
    62     for(int i=1;i<=n;i++)
    63         for(int j=1;j<=m;j++)
    64         {
    65             if(a[i][j])continue;
    66             int x=cal(i,j);
    67             if(!a[i][j-1])add(x,cal(i,j-1),1);
    68             if(!a[i][j+1])add(x,cal(i,j+1),1);
    69             if(!a[i-1][j])add(x,cal(i-1,j),1);
    70             if(!a[i+1][j])add(x,cal(i+1,j),1);
    71             int t1=lower_bound(c[j].begin(),c[j].end(),i)-c[j].begin();
    72             int t2=lower_bound(r[i].begin(),r[i].end(),j)-r[i].begin();
    73             int d1=min(c[j][t1]-i,i-c[j][t1-1]),d2=min(r[i][t2]-j,j-r[i][t2-1]);
    74             int md=min(d1,d2);
    75             if(c[j][t1]-1!=i)   add(x,cal(c[j][t1]-1,j),md);
    76             if(c[j][t1-1]+1!=i) add(x,cal(c[j][t1-1]+1,j),md);
    77             if(r[i][t2]-1!=j)   add(x,cal(i,r[i][t2]-1),md);
    78             if(r[i][t2-1]+1!=j) add(x,cal(i,r[i][t2-1]+1),md);
    79         }
    80     spfa();
    81     int ans=d[en];
    82     if(ans>=INF)puts("no");
    83     else printf("%d
    ",ans);
    84 }
    View Code

     T3[C. 柱状图]

    引问:1-N的任意序列,每个数加上或减去一个值,使的所有的数成为一个相同的数,求变化值之和的最小值

    引理:变化后的数为这个序列中位数,此时变化值之和最小

    证明:先将这个序列排序,设变化后的相同值为p,则小于p的所有数变化值之和为$1+2+...+(p-1)$大于p的数变化值之和为$1+2+...+n-p+1$

    等差求和并化简为$p^2-(n+1)p+(n+n^2)/2$,单谷函数,由图像,取$(n+1)/2$时取到最小值

    推广:任意一个序列,进行以上操作,都要变为这个序列的中位数,才能使解最优

    30%算法 n^3暴力

    最外层枚举屋顶的位置,然后枚举屋顶高度,再扫一边统计答案

    60%算法

    最外层枚举屋顶的位置,思考如何将枚举屋顶高度的过程优化,

    定义 s[i]=a[i]+abs(pos-i)  ,H为最高高度,det[i]为其变化的高度;

    a[i]+abs(pos-i)=H-det[i],左式形成了一个序列,将左式序列变化det值使得它成为一个定值H

    使用以上证明,所以就是找这个序列的中位数$O(n^2)$

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<cmath>
     5 #include<algorithm>
     6 #define R register
     7 #define ll long long
     8 using namespace std;
     9 const int maxn=100005;
    10 inline int read()
    11 {
    12     int f=1,x=0;char ch=getchar();
    13     while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
    14     while(ch<='9'&&ch>='0'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
    15     return f*x;
    16 }
    17 inline int ab(int x){return (x<0)?(-x):x;} 
    18 int n,a[maxn],s[maxn];
    19 int main()
    20 {
    21 //    freopen("data","r",stdin);
    22     n=read();
    23     int mx=n;
    24     for(R int i=1;i<=n;++i)
    25         a[i]=read(),mx=max(a[i],mx);
    26     R ll ans=0x3f3f3f3f3f3f3f3f;
    27     for(R int pos=1;pos<=n;++pos)
    28     {
    29         for(R int i=1;i<=n;++i)    s[i]=a[i]+ab(pos-i);
    30         R int mid=(n+1)>>1;
    31         nth_element(s+1,s+mid,s+n+1);
    32         R int val=s[mid],mx=max(pos,n-pos+1);
    33         val=max(val,mx);
    34         ll tot=0;
    35         for(R int i=1;i<=n;i++)
    36             tot+=ab(val-s[i]);
    37         ans=min(ans,tot);
    38     }
    39     printf("%lld
    ",ans);
    40 }
    View Code

    理论60%算法2

    发现一个性质:变化的总值sum  关于 屋顶高度h 的图像为单谷函数

    可以这么想:如果屋顶高度特别高,那么每一个i都是增大的,sum就会随h单调递增,反之递减,当h适中时,代价会最小

    用三分搞一下

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 long long n,h[100010],ans=0x7fffffffffffffff,now,k,maxx;
     4 long long check(const int he,const int pos){
     5     long long an=0;
     6     for(int i=1;i<=n;i++)
     7         an+=abs(he-abs(pos-i)-h[i]);
     8     return an;
     9 }
    10 int main(){
    11     scanf("%lld",&n);
    12     for(int i=1;i<=n;i++) scanf("%lld",&h[i]),maxx=max(maxx,h[i]);
    13     for(int i=1;i<=n;i++){
    14         long long l=n,r=maxx*3;
    15         while(l<r-2){
    16             long long lmid=l+(r-l)/3,rmid=l+(r-l)*2/3;
    17             if(check(lmid,i)<=check(rmid,i)) r=rmid;
    18             else l=lmid;
    19         }
    20         //cout<<i<<" "<<l<<endl;
    21         ans=min(ans,check(l,i));
    22         ans=min(ans,check(l+1,i));
    23         ans=min(ans,check(l+2,i));
    24     }
    25     printf("%lld",ans);
    26 }
    mikufun

    100%算法:「乱搞」「模拟退火」

    打表发现,sum关于屋顶位置是个多谷函数(就是没有任何规律)

    模拟退火可A

     1 #include<bits/stdc++.h>
     2 #include<iostream>
     3 #include<cstdio>
     4 #include<cstdlib>
     5 #include<cstring>
     6 #include<cmath>
     7 #include<algorithm>
     8 #define R register
     9 #define ll long long
    10 using namespace std;
    11 const int maxn=100005;
    12 const double eps=1e-5;
    13 const double delta=0.983;
    14 const double jl=1;
    15 inline int read()
    16 {
    17     int f=1,x=0;char ch=getchar();
    18     while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
    19     while(ch<='9'&&ch>='0'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
    20     return f*x;
    21 }
    22 inline int ab(int x){return (x<0)?(-x):x;}
    23 int n,a[maxn],s[maxn];
    24 ll ans=0x3f3f3f3f3f3f3f3f;
    25 inline ll cal(int pos)
    26 {
    27     for(int i=1;i<=n;i++)    s[i]=a[i]+ab(pos-i);
    28     int mid=(n+1)>>1;
    29     nth_element(s+1,s+mid,s+n+1);
    30     int val=s[mid],mx=max(pos,n-pos+1);
    31     val=max(val,mx);
    32     ll tot=0;
    33     for(int i=1;i<=n;i++)
    34         tot+=ab(val-s[i]);
    35     return tot;
    36 }
    37 void SA()
    38 {
    39     double T=1000;
    40     int now=(n+1)>>1;
    41     ll nowans=cal(now);
    42     while(T>eps)
    43     {
    44         int tmp=now+(2LL*rand()-RAND_MAX)*T*0.000001;
    45         if(T<jl) tmp=max(tmp,1),tmp=min(tmp,n);
    46         else tmp=(tmp%n+n)%n+1;
    47         ll tmpans=cal(tmp);
    48         ll DE=tmpans-nowans;
    49         if(DE<0||exp(-DE/T)*RAND_MAX>rand())nowans=tmpans,now=tmp;
    50         if(tmpans<ans)ans=tmpans;
    51         T*=delta;
    52     }
    53 }
    54 int main()
    55 {
    56     //freopen("data","r",stdin);
    57     srand(time(0));
    58     n=read();
    59     for(R int i=1;i<=n;++i)
    60         a[i]=read();
    61     SA();
    62     printf("%lld
    ",ans);
    63     return 0;
    64 }
    View Code

    推了一下午的式子,然后发现,最一开始的定义就有些问题,所以努力无果QAQ

    自己瞎颓式子的时候一定要严谨证明

    有一些东西不必要用个式子表示,用理说明(感性理解)就好

    打表打表找规律

    愿你在迷茫时,记起自己的珍贵。
  • 相关阅读:
    【AGC010 C】Cleaning
    【未知来源】火神的鱼
    【2017 北京集训 String 改编版】子串
    【未知来源】记忆
    【2017 江苏集训】子串
    【未知来源】循环移位
    【未知来源】K-th String
    【hdu 6067】Big Integer
    【CERC 2014 E】2048
    【hdu 6155】Subsequence Count
  • 原文地址:https://www.cnblogs.com/casun547/p/11319133.html
Copyright © 2011-2022 走看看