2018.6.12 提高组模拟

T1:两张01表,问能否对应上(不翻转,可平移,留下印记1的地方不能再留,模子的1不能超出鼎面)

暴力模拟;

原来的(考场上写的)愚蠢错误暴力就不要管了吧...

只记录1的位置,然后去用模子最左上角的1对应鼎面最左上角的1,因为左上角只能由左上角对应;

然而还是T了3个点(注释中的写法);

又改了几下,改成子函数好让它直接 return ,读入时候直接读一行字符串而不是“%1d”,就过了最后的3个点;

改后(AC)代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
using namespace std;
int const maxn=1e6+5;
int T,n,m,a,b,cns,cnt,h[1005][1005];
bool flag;
char dc[maxn];
struct N{int x,y;}s[maxn],t[maxn];
int rd()
{
    int ret=0;char ch=getchar();
    while(ch<'0'||ch>'9')ch=getchar();
    while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar();
    return ret;
}
void solve()
{
    n=rd();m=rd();a=rd();b=rd();
    cns=0;cnt=0;
    for(int i=1;i<=n;i++)
    {    
        scanf("%s",&dc); //直接输入一行 
        for(int j=1,x;j<=m;j++)//
        {
//            scanf("%1d",&x);
            if(dc[j-1]=='1')s[++cns].x=i,s[cns].y=j,h[i][j]=1;
        }
    }
    int x1,y1;
    for(int i=1;i<=a;i++)
    {
        scanf("%s",&dc);
        for(int j=1,x;j<=b;j++)//
        {
//            scanf("%1d",&x);
            if(dc[j-1]=='1'&&!cnt)x1=i,y1=j;
            if(dc[j-1]=='1')t[++cnt].x=i-x1,t[cnt].y=j-y1;
        }
    }
    int x,y,xx,yy;
    for(int i=1;i<=cns;i++)//
    {
        x=s[i].x;y=s[i].y;
        if(!h[x][y])continue;//
        for(int j=1;j<=cnt;j++)//
        {
            xx=x+t[j].x;yy=y+t[j].y;
//            int b=bh[xx][yy];
//            if(!b||(b&&vis[b]))//此位为 0 或已经有 1 
//            {
//                flag=1;break;
//            }
            if(xx<1||yy<1||xx>n||yy>m||!h[xx][yy])
            {
                printf("NO\n");return;//用子函数则可以直接return 
            }
            h[xx][yy]=0;//
        }
    }
//    if(!flag)
//        for(int i=1;i<=cns;i++)
//            if(!vis[i])
//            {
//                flag=1;break;
//            }
    printf("YES\n");
}
int main()
{
    freopen("grain.in","r",stdin);
    freopen("grain.out","w",stdout);
    T=rd();
    while(T--)solve();
    return 0;
} 

T2:LCIS裸题

考场上却忘记了LCIS的模板!

然后还改了半天,主要是记录路径的方法,记得自己原来就似懂非懂,这下终于牢牢记住了!

改后WA代码(路径记录不对):

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
using namespace std;
typedef long long ll;
int const maxn=5005;
int pre[maxn],ans[maxn],f[maxn][maxn];
ll n,m,a[maxn],b[maxn];
ll rd()
{
    ll ret=0;char ch=getchar();
    while(ch<'0'||ch>'9')ch=getchar();
    while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar();
    return ret;
}
int main()
{
    freopen("sail.in","r",stdin);
    freopen("sail.out","w",stdout);
    n=rd();
    for(int i=1;i<=n;i++)a[i]=rd();
    m=rd();
    for(int i=1;i<=m;i++)b[i]=rd();
    for(int i=1;i<=n;i++)
    {
        int mx=0,p=0;
        for(int j=1;j<=m;j++)
        {
            f[i][j]=f[i-1][j];
            if(b[j]<a[i]&&mx<f[i-1][j])
            {
                mx=f[i-1][j];p=j;
            }
            if(b[j]==a[i])
            {
                f[i][j]=mx+1;pre[j]=p;
            }
        }
    }
    int mx=0,t,k;
    for(int j=1;j<=m;j++)
        if(mx<f[n][j])mx=f[n][j],t=j;
    for(;t;t=pre[t])ans[++k]=t;
    printf("%d\n",mx);
    for(int i=k;i;i--)printf("%lld ",b[ans[i]]);
    return 0;
}

改后(AC)代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
using namespace std;
typedef long long ll;
int const maxn=5005;
int pre[maxn][maxn],ans[maxn],f[maxn][maxn],t;
ll n,m,a[maxn],b[maxn];
ll rd()
{
    ll ret=0;char ch=getchar();
    while(ch<'0'||ch>'9')ch=getchar();
    while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar();
    return ret;
}
void work(int x,int y,int k)
{
    if(!k)return;
    while(a[x]!=b[y])x--;
    work(x,pre[x][y],k-1);
    printf("%lld ",b[y]);
}
int main()
{
    freopen("sail.in","r",stdin);
    freopen("sail.out","w",stdout);
    n=rd();
    for(int i=1;i<=n;i++)a[i]=rd();
    m=rd();
    for(int i=1;i<=m;i++)b[i]=rd();
    int ans=0;
    for(int i=1;i<=n;i++)
    {
        int mx=0,p=0;
        for(int j=1;j<=m;j++)
        {
            f[i][j]=f[i-1][j];
            if(b[j]<a[i]&&mx<f[i-1][j])
            {
                mx=f[i-1][j];p=j;
            }
            if(b[j]==a[i])
            {
                f[i][j]=mx+1;pre[i][j]=p;
            }
            if(ans<f[i][j])ans=f[i][j],t=j;
        }
    }
    printf("%d\n",ans);
    if(ans)work(n,t,ans);printf("\n");
    return 0;
}

 T3:查询一个区间内某个数加和的最大值(相等的数相加)

考场上只想出了暴力,而且都忘记离散化,用的 map ...

正解是莫队,后来讨论出来了;

先写了个60分,以为不行了,去看TJ;

TJ的莫队没有排序、按块走,但也能过:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
using namespace std;
typedef long long ll;
int const maxn=1e5+5,K=320;
int n,m,a[maxn],b[maxn],tmp[maxn],ct=1,t;
ll sum[maxn],ans[maxn],mx,s[maxn],num[maxn];
ll f[maxn/K+2][maxn/K+2],g[maxn/K+2][maxn];
//struct N{int l,r,bh;}q[maxn];
//bool cmp(N x,N y){return x.l<y.l;}
//bool cmp2(int x,int y){return q[x].r<q[y].r;}
int rd()
{
    int ret=0;char ch=getchar();
    while(ch<'0'||ch>'9')ch=getchar();
    while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar();
    return ret;
}
int main()
{
    freopen("mode.in","r",stdin);
    freopen("mode.out","w",stdout);
    n=rd();m=rd();
//    K=sqrt(n);
    for(int i=1;i<=n;i++)a[i]=rd(),b[i]=a[i];
    sort(b+1,b+n+1);int cnt=unique(b+1,b+n+1)-b-1;
    for(int i=1;i<=n;i++)
    {
        int k=lower_bound(b+1,b+cnt+1,a[i])-b;
        num[i]=a[i];a[i]=k;
        g[(i-1)/K+1][k]+=num[i];//前缀和 
    }
    for(int i=1;(i-1)*K<n;i++)
        for(int j=1;j<=cnt;j++)g[i][j]+=g[i-1][j];
    for (int i=1;(i-1)*K<n;i++) //块 i
    {
        memset(sum,0,sizeof sum);
        for (int j=i,k=(i-1)*K+1;(j-1)*K<n;j++) {//第 i 块之后的块 
               f[i][j]=f[i][j-1];//从第 i 块到第 j 块的 mx 
            for (;k<=j*K&&k<=n;k++) {//第 j 块中的点 
                sum[a[k]]+=num[k];
                f[i][j]=max(f[i][j],sum[a[k]]);
            }
        }
    }
    memset(sum,0,sizeof sum);//
    for(int i=1;i<=m;i++)
    {
//        memset(sum,0,sizeof sum);
        ll ans=0;
        int l=rd(),r=rd();
        int bl=(l-1)/K+1,br=(r-1)/K+1;
        if(br-bl<=1)
        {
            for(int j=l;j<=r;j++)
                sum[a[j]]+=num[j],ans=max(ans,sum[a[j]]);
            for(int j=l;j<=r;j++)
                sum[a[j]]=0;
        }
        else
        {
            ans=f[bl+1][br-1];
            for(int j=l;j<=bl*K;j++)sum[a[j]]+=num[j];
            for(int j=(br-1)*K+1;j<=r;j++)sum[a[j]]+=num[j];
            for(int j=l;j<=bl*K;j++)ans=max(ans,sum[a[j]]+g[br-1][a[j]]-g[bl][a[j]]);
            for(int j=(br-1)*K+1;j<=r;j++)ans=max(ans,sum[a[j]]+g[br-1][a[j]]-g[bl][a[j]]);
            for(int j=l;j<=bl*K;j++)sum[a[j]]=0;
            for(int j=(br-1)*K+1;j<=r;j++)sum[a[j]]=0;
        }
        printf("%lld\n",ans);
    }
    return 0;
}
TJ

然后发现影响速度很大的一个地方就是每次的 memset ,所以把原来的60分代码的 memset 都改成循环给修改过的地方赋0,就可以A了! 

而我写的排序分块(正版)莫队当然比TJ更快了!

改后(AC)代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
using namespace std;
typedef long long ll;
int const maxn=1e5+5;
int n,m,a[maxn],b[maxn],K,tmp[maxn],ct=1,t;
ll sum[maxn],ans[maxn],mx,s[maxn],num[maxn];
struct N{int l,r,bh;}q[maxn];
bool cmp(N x,N y){return x.l<y.l;}
bool cmp2(int x,int y){return q[x].r<q[y].r;}
int rd()
{
    int ret=0;char ch=getchar();
    while(ch<'0'||ch>'9')ch=getchar();
    while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar();
    return ret;
}
void solve(int k)//第k块 
{
    memset(sum,0,sizeof sum);
    sort(tmp+1,tmp+t+1,cmp2);
    int L=k*K+1,R=L;
    mx=0;
    memset(s,0,sizeof s);
    for(int i=1;i<=t;i++)
    {
//        memset(s,0,sizeof s);
        ll tmx=0;
        int j=tmp[i],l=q[j].l,r=q[j].r;
        for(int nw=R;nw<=r;nw++)
        {
            sum[a[nw]]+=num[a[nw]];
            mx=max(mx,sum[a[nw]]);
        }
        R=r+1;
        for(int nw=L-1;nw>=l;nw--)
        {
            s[a[nw]]+=num[a[nw]];
            tmx=max(tmx,max(mx,s[a[nw]]+sum[a[nw]]));
        }
        for(int nw=L-1;nw>=l;nw--)s[a[nw]]=0;
        ans[q[j].bh]=tmx;
    }
}
int main()
{
    freopen("mode.in","r",stdin);
    freopen("mode.out","w",stdout);
    n=rd();m=rd(); K=sqrt(n);
    for(int i=1;i<=n;i++)a[i]=rd(),b[i]=a[i];
    sort(b+1,b+n+1);int cnt=unique(b+1,b+n+1)-b-1;
    for(int i=1;i<=n;i++)
    {
        int k=lower_bound(b+1,b+cnt+1,a[i])-b;
        num[k]=a[i];a[i]=k;
    }
    for(int i=1;i<=m;i++)q[i].l=rd(),q[i].r=rd(),q[i].bh=i;
    sort(q+1,q+m+1,cmp);
    for(int i=1;(i-1)*K<n;i++)
    {
        t=0;
        for(int j=ct;q[j].l<=i*K&&j<=m;j++)tmp[++t]=j,ct=j+1;//tmp 装着第 i 块的 q 
        solve(i);
    }
    for(int i=1;i<=m;i++)
        printf("%lld\n",ans[i]);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Zinn/p/9181256.html