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; }
然后发现影响速度很大的一个地方就是每次的 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; }