2017/10/21模拟赛总结

来源:互联网 发布:淘宝链接转换淘口令 编辑:程序博客网 时间:2024/06/05 03:17

T1 完美01正方形

枚举端点前缀和判断一下就好了
O(n3)
注意判断的条件

#include<bits/stdc++.h>using namespace std;#define N 310int a[N][N];int calc(int l1,int l2,int r1,int r2){    return a[l2][r2]-a[l1-1][r2]-a[l2][r1-1]+a[l1-1][r1-1];}int main(){    int n,m,i,j,k,ans=0;    scanf("%d%d",&n,&m);    for (i=1;i<=n;i++)        for (j=1;j<=m;j++){            scanf("%d",&a[i][j]);            a[i][j]+=a[i-1][j]+a[i][j-1]-a[i-1][j-1];        }    for (i=1;i<=n;i++)        for (j=1;j<=m;j++)            for (k=1;k+i<=n && k+j<=m;k++){                int i1=i+k,j1=j+k;                if (calc(i,i1,j,j)!=k+1) continue;                if (calc(i,i1,j1,j1)!=k+1) continue;                if (calc(i,i,j,j1)!=k+1) continue;                if (calc(i1,i1,j,j1)!=k+1) continue;                if (calc(i+1,i1-1,j+1,j1-1)!=(k-1)*(k-1)/2 && calc(i+1,i1-1,j+1,j1-1)!=((k-1)*(k-1)+1)/2) continue;                ans++;            }    printf("%d\n",ans);    return 0;}

T2 重温LIS

可以从左到右 从右到左各做一遍LIS
如果左右LIS相加长度等于整个LIS 那么就说明在LIS上
如果左右LIS方案数相乘等于整个LIS的方案数 说明这个点在所有的LIS上
由于方案数巨大 可以对质数取模
O(nlogn)

#include<bits/stdc++.h>using namespace std;#define N 100010#define mo 1000000007int a[N],f[N],lis,n;long long lisnum,g[N];void cmp(int &x,long long &y,int x1,long long y1){    if (x<x1) x=x1,y=y1;    else if (x==x1) y=(y+y1)%mo;}struct Binary_Indexed_Tree_1{    int bit[N];    long long num[N];    void add(int i,int x,long long y){        while (i<N){            cmp(bit[i],num[i],x,y);            i+=i&-i;        }    }    void query(int i,int &res1,long long &res2){        res1=res2=0;        while (i){            cmp(res1,res2,bit[i],num[i]);            i-=i&-i;        }    }}BIT1;struct Binary_Indexed_tree_2{    int bit[N];    long long num[N];    void add(int i,int x,long long y){        while (i){            cmp(bit[i],num[i],x,y);            i-=i&-i;        }    }    void query(int i,int &res1,long long &res2){        res1=res2=0;        while (i<N){            cmp(res1,res2,bit[i],num[i]);            i+=i&-i;        }    }}BIT2;int ans[N];int main(){    int i;    scanf("%d",&n);    for (i=1;i<=n;i++){        scanf("%d",&a[i]);        BIT1.query(a[i]-1,f[i],g[i]);        if (!f[i]) g[i]++;        f[i]++;        BIT1.add(a[i],f[i],g[i]);        cmp(lis,lisnum,f[i],g[i]);    }    for (i=n;i;i--){        int sum1;        long long sum2;        BIT2.query(a[i]+1,sum1,sum2);        if (!sum1) sum2++;        if (sum1+f[i]==lis){            if (g[i]*sum2%mo==lisnum) ans[i]=3;            else ans[i]=2;        }else ans[i]=1;        BIT2.add(a[i],sum1+1,sum2);    }    for (i=1;i<=n;i++) printf("%d",ans[i]);    return 0;}

T3 画画

解法1

首先可以想到用二分最大值 把本题变成一个判定性问题
有了最大值 肯定是尽可能往后跳 用尺取预处理出第一行 第二行 两行从一个位置开始最多跳到哪里
dpi,j表示第一行用了i格 第二行用了j格 最少用多少画
分三种情况讨论即可
O(n2logS)其中S=ai
考虑把两个情况放在一起考虑 即dpi表示前i列都被填满 最少用多少画
每次最多往前跳m步 跳较大的那一行
然后从较大的那个地方转移(因为不知道覆盖较小的那个地方还需要多少画)
实现细节见代码
O(nmlogS)

#include<bits/stdc++.h>using namespace std;#define N 500010#define INF (0x3f3f3f3f3f3f3f3f)void rd(int &x){    char c;x=0;    while (c=getchar(),c<48);    do x=(x<<1)+(x<<3)+(c^48);    while (c=getchar(),c>=48);}int a[N],b[N],n,m;long long suma[N],sumb[N];struct P3{    int dp[N],f[N],g[N],h[N];    bool check(long long x){        int i,j,k;        int l,r;        for (l=0,r=1;r<=n;r++){            while (suma[r]-suma[l]>x) l++;            f[r]=l;        }        for (l=0,r=1;r<=n;r++){            while (sumb[r]-sumb[l]>x) l++;            g[r]=l;        }        for (l=0,r=1;r<=n;r++){            while (suma[r]-suma[l]+sumb[r]-sumb[l]>x) l++;            h[r]=l;        }        memset(dp,63,sizeof(dp));        dp[0]=0;        for (i=1;i<=n;i++){            dp[i]=dp[h[i]]+1;            j=i,k=i;            int cnt=0;            while (cnt<m && (j || k)){                cnt++;                if (j>=k) j=f[j];                else k=g[k];                dp[i]=min(dp[i],dp[max(j,k)]+cnt);            }        }        return dp[n]<=m;    }           void solve(){        long long l=1,r=suma[n]+sumb[n];        long long res=r;        while (l<=r){            int mid=(l+r)>>1;            if (check(mid)) res=mid,r=mid-1;            else l=mid+1;        }        printf("%lld\n",res);    }}P100;int main(){    int i;    rd(n),rd(m);    for (i=1;i<=n;i++) rd(a[i]),suma[i]=suma[i-1]+a[i];    for (i=1;i<=n;i++) rd(b[i]),sumb[i]=sumb[i-1]+b[i];    P100.solve();    return 0;}

解法2

考虑用画的数量定义 即dpi表示用了i幅画 最多填到多少列
类似上面的方法向后跳
O(m2logS)

#include<bits/stdc++.h>using namespace std;#define N 20010#define M 110#define INF (0x3f3f3f3f3f3f3f3f)void rd(int &x){    char c;x=0;    while (c=getchar(),c<48);    do x=(x<<1)+(x<<3)+(c^48);    while (c=getchar(),c>=48);}int a[N],b[N],n,m;long long suma[N],sumb[N];struct P3{    int dp[M],f[N],g[N],h[N];    bool check(long long x){        int i,j,k;        int l,r;        for (l=0,r=1;l<=n;l++){            while (r<n && suma[r+1]-suma[l]<=x) r++;            f[l]=r;        }        for (l=0,r=1;l<=n;l++){            while (r<n && sumb[r+1]-sumb[l]<=x) r++;            g[l]=r;        }        for (l=0,r=1;l<=n;l++){            while (r<n && suma[r+1]-suma[l]+sumb[r+1]-sumb[l]<=x) r++;            h[l]=r;        }        memset(dp,0,sizeof(dp));        dp[0]=0;        for (i=0;i<m;i++){            j=k=dp[i];            dp[i+1]=max(dp[i+1],h[j]);            int cnt=0;            while (i+cnt<m && (j<n || k<n)){                cnt++;                if (j<=k) j=f[j];                else k=g[k];                dp[i+cnt]=max(dp[i+cnt],min(j,k));            }        }        return dp[m]==n;    }           void solve(){        long long l=1,r=suma[n]+sumb[n];        long long res=r;        while (l<=r){            int mid=(l+r)>>1;            if (check(mid)) res=mid,r=mid-1;            else l=mid+1;        }        printf("%lld\n",res);    }}P100;int main(){    int i;    rd(n),rd(m);    for (i=1;i<=n;i++) rd(a[i]),suma[i]=suma[i-1]+a[i];    for (i=1;i<=n;i++) rd(b[i]),sumb[i]=sumb[i-1]+b[i];    P100.solve();    return 0;}

这次T2和T3都是从经典问题提升而来的 解题时应该要考虑以前的解决方式 加以改进和优化

Date:2017/10/23
By CalvinJin