QZS8.21

T1

暴力(AC)

O(n^2*k) 50

稍微优化了一些 ,就是搞个二维前缀和(白色为1,黑色为0),O(N^2) 枚举左上角,然后每一行 每一列的处理,先减去原来那块的值,再加上k,统计是否为n, 总体复杂度O(n^2*k)

啊满分what???(kao考试数组开小了,100变50啊啊啊)话说数据好水

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=2005; 
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
	return f*x;
}
int n,k,ans,res=0;
int s[N][N];
char c[N];
int main() {
    n=read();k=read();
    for(int i=1;i<=n;i++) {
        scanf("%s",c+1);
        for(int j=1;j<=n;j++)
            s[i][j]=(c[j]=='W');
    }
    for(int i=1;i<=n;i++) 
        for(int j=1;j<=n;j++)
            s[i][j]+=s[i-1][j]+s[i][j-1]-s[i-1][j-1];
    
    for(int i=1;i<=n;i++) 
        if(s[i][n]==n) ans++;
    for(int i=1;i<=n;i++) 
        if(s[n][i]==n) ans++;
    int pre,sum,mx;
    for(int i=1;i<=n-k+1;i++)
        for(int j=1;j<=n-k+1;j++) {
            mx=ans;
            for(int p=i;p<i+k;p++) {//横着
                pre=s[p][j+k-1]+s[p-1][j-1]-s[p-1][j+k-1]-s[p][j-1];
                sum=s[p][n]-s[p-1][n]-pre+k;
                if(sum==n&&s[p][n]-s[p-1][n]!=n) mx++;
            }
            for(int p=j;p<j+k;p++) {
                pre=s[i+k-1][p]+s[i-1][p-1]-s[i-1][p]-s[i+k-1][p-1];
                sum=s[n][p]-s[n][p-1]-pre+k;
                if(sum==n&&s[n][p]-s[n][p-1]!=n) mx++;
            }
            res=max(mx,res);
        }
     printf("%d\n",res);
}
/*
4 2
BWWW
WBBW
WBBW
WWWB 
*/
 

正解

差分思想

考虑每个点成为最后求的k*k矩阵的左端点所能对ans的贡献

我们要找的是贡献最大的点,

正着想是枚举左端点然后去算对ans的贡献

那么我们其实可以反着想——

如果某一行/列能够被k个点覆盖后完全为白色,那么我们可以找出 能把这行/列覆盖为白色的左端点的位置矩阵,对于这个矩阵每个值(设为s)+1,然后这里矩阵加一就用到了差分的思想,

具体的见下图

image-20200821150653628

inline void add(int x,int y,int xx,int yy) {
    ++s[x][y];
    --s[x][yy+1];
    --s[xx+1][y];
    ++s[xx+1][yy+1];
}

O(n^2)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=2005; 
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
	return f*x;
}
int n,k,ans=0,res=0;
int s[N][N];
char mp[N][N];
bool flag;
inline void Max(int &x,int y) {if(x<y)x=y;}
inline void Min(int &x,int y) {if(x>y)x=y;}
inline void add(int x,int y,int xx,int yy) {
    ++s[x][y];
    --s[x][yy+1];
    --s[xx+1][y];
    ++s[xx+1][yy+1];
}
int main() {
    n=read();k=read();
    for(int i=1;i<=n;i++)
        scanf("%s",mp[i]+1);
    for(int i=1;i<=n;i++) {//横着
        int l=1,r=n,flag=0;
        for(int j=1;j<=n;j++) {
            if(mp[i][j]=='B') {
                Max(l,j-k+1);
                Min(r,j);
                flag=1;
            }
        }
        if(!flag) {++ans;continue;}
        if(l<=r)add(max(1,i-k+1),l,i,r);
    }
    for(int j=1;j<=n;j++) {//竖着
        int l=1,r=n,flag=0;
        for(int i=1;i<=n;i++) {
            if(mp[i][j]=='B') {
                Max(l,i-k+1);
                Min(r,i);
                flag=1;
            }
        }
        if(!flag) {++ans;continue;}
        if(l<=r)add(l,max(1,j-k+1),r,j);
    }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            s[i][j]+=s[i-1][j]+s[i][j-1]-s[i-1][j-1];
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            Max(res,ans+s[i][j]);
    printf("%d\n",res);
    return 0;
}

T2

O(n^2) 暴力

dp+st表!!!爆零???啊啊啊啊?算法错了???

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=5005; 
const int mod=1<<30; 
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
	return f*x;
}
int n,x,y,z,m;
int b,b1,b2,p_pre,p_now,l,r;
int mi[N][N],mx[N][N];
struct RMQ {
    int log2[N];
    void init() {
        log2[0]=-1;
        for(int i=1;i<=n;i++) log2[i]=log2[i>>1]+1;
        for(int j=1;j<30;j++)
            for(int i=1;i+(1<<j)<=n+1;i++) {
                mx[i][j]=max(mx[i][j-1],mx[i+(1<<(j-1))][j-1]);
                mi[i][j]=min(mi[i][j-1],mi[i+(1<<(j-1))][j-1]);
            }
    }
    int query_mx(int ql,int qr) {
        int k=log2[qr-ql+1];
        return max(mx[ql][k],mx[qr-(1<<k)+1][k]);
    }
    int query_mi(int ql,int qr) {
        int k=log2[qr-ql+1];
        return min(mi[ql][k],mi[qr-(1<<k)+1][k]);
    }
}rmq;
int f[N];
int main() {
    memset(mi,0x3f,sizeof(mi));
    n=read();
    x=read(),y=read(),z=read(),b1=read(),b2=read(),m=read();
    p_pre=0;
    int i=1;
    for(int j=1;j<=m&&i<=n;j++) {
        p_now=read();l=read();r=read();
        if(i>p_pre&&i<=p_now) {
            b=(long long)(x*b1+y*b2+z)%mod;
            mx[i][0]=mi[i][0]=b%(r-l+1)+l;
            b2=b1,b1=b;
            i++;
        }
        p_pre=p_now;
    } 
    rmq.init();
    for(i=1;i<=n;i++)
        for(int j=0;j<i;j++)
            f[i]=max(f[i],f[j]+rmq.query_mx(j+1,i)-rmq.query_mi(j+1,i));
    printf("%d\n",f[n]);
    return 0;
}

/*
5 
1 1 1 1 1 5
1 1 1
2 2 2
3 3 3
4 1 1
5 2 2
*/

正解

如果有一组解使得某一段不是单调的,那么可以把他分成两端单调的使极差之和比原来大

例子:a<b<c<d

(a,c,b,d) —— ans= d-a

(a,c,b,d) —— (a,c)(b,d) ——ans’=c-a+d-b =d-a+c-b 显然c-b>0所以 ans’>ans

我们只要O(n) DP在每个极值left/right分开即可

#include <iostream>
#include <cstdio>
using namespace std;
const int N=10000005; 
const int inf=0x3f3f3f3f3f3f3f3f; 
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
	return f*x;
}

int n,x,y,z,m;
int b[N],pos,r,a[N];
long long ans[2];
int main() {
    n=read();
    x=read(),y=read(),z=read(),b[1]=read(),b[2]=read(),m=read();
    pos=0;
    for(int i=3;i<=n;i++)
        b[i]=(1ll*x*b[i-1]+1ll*y*b[i-2]+z)&((1<<30)-1);
    int np,l,r;
    for(int i=1;i<=m;i++) {
        np=read();l=read();r=read();
        while(pos<np) {
            ++pos;
            a[pos]=b[pos]%(r-l+1)+l;
        }
    } 
    ans[0]=-inf;ans[1]=0;
    int mx=0,mi=0x3f3f3f3f;
    for(int i=1;i<=n;i++) {
        if(i>1 && i<n && ((a[i]<a[i-1]&&a[i]<=a[i+1])||(a[i]>a[i-1]&&a[i]>=a[i+1]))) {//极值点
            long long l=-inf,r=-inf;//l,r表示在左边和右边划分的ansmax
            l=max(l,ans[0]+max(mx,a[pos])-min(mi,a[pos]));
            if(pos!=i-1) l=max(l,ans[1]+mx-mi);
            else l=max(l,ans[1]);
            r=max(r,ans[0]+max(mx,max(a[i],a[pos]))-min(mi,min(a[i],a[pos])));
            r=max(r,ans[1]+max(a[i],mx)-min(a[i],mi));
            ans[0]=l,ans[1]=r;pos=i;
            mx=0,mi=0x3f3f3f3f;
        } else 
            mx=max(mx,a[i]),mi=min(mi,a[i]);//mx,mi是这段区间的
    }
    printf("%lld\n",max(ans[0]+max(mx,a[pos])-min(mi,a[pos]),ans[1]+mx-mi));
    return 0;
}

T3

gugugu

posted @ 2020-09-13 07:38  ke_xin  阅读(20)  评论(0编辑  收藏  举报