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,然后这里矩阵加一就用到了差分的思想,
具体的见下图
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