考试过程:这次考试觉得自己发挥不错,就是还是有一些没想到。
首先是T1,看到数据范围我觉得正解应该是\(o(n^3)\)的,于是就往这个方面想,想到了并查集,但是利用并查集需要把矩形里面的除去,我当时觉得这种方法不太可行,就换了一种。
想到了枚举小矩形后再从小矩形四个边上向外扩展,最后求和,但是我算错复杂度了,这样是\(o(n^4)\)的。
然而正解是将我的两个思路合在一起(难受及了)。
然后是T2,凭直觉我觉得是一个\(dp\),而且看数据范围我觉得\(dp\)方程应该长这个样子\(f_i=max(f_j+1)\),
然后我就想了想,真的退出来了,这样复杂度是\(o(n^2)\)的,可以获得\(50pts\),但是我的方程获得了\(70pts\),比别人多\(20pts\),可能是我的一些剪枝。
然后想优化,写写式子就发现是个\(CDQ\)分治的板子题,然后就切了。
T3,T4觉得不太可做,就想打暴力,结果没什么时间了,后两题就没得到什么分,这点以后需要注意,无论什么题,基础分必须要拿到。
T1 网格图
思路:
说一下这个计算的过程,我们对于每一行,先枚举一个\(k\times k\)的矩形,暴扫,然后将矩形内部的点所属的并查集的\(size--\),然后我们扫这个矩形的上下左右四个边界,加上他们所在的并查集的大小,最后再加上\(k\times k\)即可。考虑移动的话我们就要先消除之前的影响,然后添加新的影响。代码如下:
AC_code
#include<bits/stdc++.h>
#define re register int
#define ii inline int
#define iv inline void
#define mid ((l+r)>>1)
#define lc (rt<<1)
#define rc (rt<<1|1)
#define f() cout<<"fuck"<<endl
#define head headddd
#define next net
#define F first
#define S second
using namespace std;
const int N=510;
const int M=3e5+10;
int n,k,ans,timi;
char ch[N][N];
int pos[N][N],size[M],use[M];
pair<int,int> be[N][N];
bool vis[N][N];
ii read()
{
int x=0;char ch=getchar();bool f=1;
while(ch<'0' or ch>'9')
{
if(ch=='-') f=0;
ch=getchar();
}
while(ch>='0' and ch<='9')
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return f?x:(-x);
}
iv dfs(int x,int y,int fx,int fy)
{
be[x][y]=make_pair(fx,fy);
vis[x][y]=1;
size[pos[fx][fy]]++;
if(x-1>0 and ch[x-1][y]=='.' and !vis[x-1][y])
dfs(x-1,y,fx,fy);
if(x+1<=n and ch[x+1][y]=='.' and !vis[x+1][y])
dfs(x+1,y,fx,fy);
if(y-1>0 and ch[x][y-1]=='.' and !vis[x][y-1])
dfs(x,y-1,fx,fy);
if(y+1<=n and ch[x][y+1]=='.' and !vis[x][y+1])
dfs(x,y+1,fx,fy);
}
signed main()
{
freopen("grid.in","r",stdin),freopen("grid.out","w",stdout);
n=read(),k=read();
for(re i=1;i<=n;i++)
scanf("%s",ch[i]+1);
for(re i=1;i<=n;i++)
{
for(re j=1;j<=n;j++)
{
pos[i][j]=(i-1)*500+j;
be[i][j]=make_pair(i,j);
}
}
for(re i=1;i<=n;i++)
{
for(re j=1;j<=n;j++)
if(!vis[i][j] and ch[i][j]=='.')
dfs(i,j,i,j);
}
bool flag=1;
int cd=0,tmp=0;
for(re i=1;i+k<=n+1;i++)
{
tmp=0;
++timi;
for(re j=i;j<=i+k-1;j++)//暴扫
{
for(re p=1;p<=k;p++)
size[pos[be[j][p].F][be[j][p].S]]--;
}
if(i-1>0)
{
for(re p=1;p<=k;p++)
{
if(ch[i-1][p]=='.')
{
if(use[pos[be[i-1][p].F][be[i-1][p].S]]!=timi) tmp+=size[pos[be[i-1][p].F][be[i-1][p].S]];
use[pos[be[i-1][p].F][be[i-1][p].S]]=timi;
}
}
}
if(i+k<=n)
{
for(re p=1;p<=k;p++)
{
if(ch[i+k][p]=='.')
{
if(use[pos[be[i+k][p].F][be[i+k][p].S]]!=timi) tmp+=size[pos[be[i+k][p].F][be[i+k][p].S]];
use[pos[be[i+k][p].F][be[i+k][p].S]]=timi;
}
}
}
if(1+k<=n)
{
for(re p=i;p<=i+k-1;p++)
{
if(ch[p][1+k]=='.')
{
if(use[pos[be[p][1+k].F][be[p][1+k].S]]!=timi) tmp+=size[pos[be[p][1+k].F][be[p][1+k].S]];
use[pos[be[p][1+k].F][be[p][1+k].S]]=timi;
}
}
}
ans=max(ans,tmp+k*k);
for(re j=2;j+k<=n+1;j++)
{
tmp=0;
++timi;
for(re p=i;p<=i+k-1;p++) //消除影响
size[pos[be[p][j-1].F][be[p][j-1].S]]++;
for(re p=i;p<=i+k-1;p++)//加入新列
size[pos[be[p][j+k-1].F][be[p][j+k-1].S]]--;
if(i-1>0)
{
for(re p=j;p<=j+k-1;p++)
{
if(ch[i-1][p]=='.')
{
if(use[pos[be[i-1][p].F][be[i-1][p].S]]!=timi) tmp+=size[pos[be[i-1][p].F][be[i-1][p].S]];
use[pos[be[i-1][p].F][be[i-1][p].S]]=timi;
}
}
}
if(i+k<=n)
{
for(re p=j;p<=j+k-1;p++)
{
if(ch[i+k][p]=='.')
{
if(use[pos[be[i+k][p].F][be[i+k][p].S]]!=timi) tmp+=size[pos[be[i+k][p].F][be[i+k][p].S]];
use[pos[be[i+k][p].F][be[i+k][p].S]]=timi;
}
}
}
if(j-1>0)
{
for(re p=i;p<=i+k-1;p++)
{
if(ch[p][j-1]=='.')
{
if(use[pos[be[p][j-1].F][be[p][j-1].S]]!=timi) tmp+=size[pos[be[p][j-1].F][be[p][j-1].S]];
use[pos[be[p][j-1].F][be[p][j-1].S]]=timi;
}
}
}
if(j+k<=n)
{
for(re p=i;p<=i+k-1;p++)
{
if(ch[p][j+k]=='.')
{
if(use[pos[be[p][j+k].F][be[p][j+k].S]]!=timi) tmp+=size[pos[be[p][j+k].F][be[p][j+k].S]];
use[pos[be[p][j+k].F][be[p][j+k].S]]=timi;
}
}
}
ans=max(ans,tmp+k*k);
}
for(re j=i;j<=i+k-1;j++)//暴扫还原
for(re p=n-k+1;p<=n;p++)
size[pos[be[j][p].F][be[j][p].S]]++;
}
printf("%d\n",ans);
return 0;
}
T2 序列问题
思路:简单的\(dp\)题,\(CDQ\)分治裸题。反正我觉得是这样。
设\(f_i\)表示以\(i\)结尾,并且\(i\)是满足条件的序列的最大长度,那么状态转移方程为\(f_i=max(f_i,f_j+1)\),注意\(j<i,a_j<a_i,i-a_i>=j-a_j\),这样的话明显的三维偏序问题真的很\(CDQ\),随便打打就过了,注意线段树要用\(n\),不要用离散化后的\(cnt\).代码如下:
AC_code
#include<bits/stdc++.h>
#define re register int
#define ii inline int
#define iv inline void
#define f() cout<<"fuck"<<endl
#define head headddd
#define next net
using namespace std;
const int N=5e5+10;
int n,ans,cnt;
int a[N],lsh[N];
int f[N];
struct node
{
int a,pos;
}cun[N];
ii read()
{
int x=0;char ch=getchar();bool f=1;
while(ch<'0' or ch>'9')
{
if(ch=='-') f=0;
ch=getchar();
}
while(ch>='0' and ch<='9')
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return f?x:(-x);
}
inline bool com1(node x,node y)
{
if(x.a!=y.a) return x.a<y.a;
return x.pos<y.pos;
}
inline bool com2(node x,node y)
{
return x.pos<y.pos;
}
struct Segment_Tree
{
#define mid ((l+r)>>1)
#define lc (rt<<1)
#define rc (rt<<1|1)
int maxx[N<<2];
iv pp(int rt)
{
maxx[rt]=max(maxx[lc],maxx[rc]);
}
iv reset(int rt,int l,int r)
{
maxx[rt]=0;
if(l==r) return;
reset(lc,l,mid),reset(rc,mid+1,r);
pp(rt);
}
iv clear(int rt,int l,int r,int p)
{
if(l==r)
{
maxx[rt]=0;
return;
}
if(mid>=p) clear(lc,l,mid,p);
else clear(rc,mid+1,r,p);
pp(rt);
}
iv add(int rt,int l,int r,int p,int z)
{
if(l==r)
{
maxx[rt]=max(maxx[rt],z);
return;
}
if(mid>=p) add(lc,l,mid,p,z);
else add(rc,mid+1,r,p,z);
pp(rt);
}
ii query(int rt,int l,int r,int L,int R)
{
if(L>R) return 0;
if(L<=l and r<=R) return maxx[rt];
if(mid>=R) return query(lc,l,mid,L,R);
if(mid<L) return query(rc,mid+1,r,L,R);
return max(query(lc,l,mid,L,R),query(rc,mid+1,r,L,R));
}
#undef mid
#undef lc
#undef rc
}T;
void solve(int l,int r)
{
if(l==r)
{
if(l>=lsh[a[l]]) f[l]=max(f[l],1);
return;
}
int mid=(l+r)>>1;
solve(l,mid);
sort(cun+l,cun+mid+1,com1),sort(cun+mid+1,cun+r+1,com1);
int j=l,i=mid+1;
for(;i<=r;i++)
{
while(cun[j].a<cun[i].a and j<=mid)
{
if(cun[j].pos-lsh[cun[j].a]>=0)
T.add(1,0,n,cun[j].pos-lsh[cun[j].a],f[cun[j].pos]);
++j;
}
if(cun[i].pos-lsh[cun[i].a]>=0)
f[cun[i].pos]=max(f[cun[i].pos],T.query(1,0,n,0,cun[i].pos-lsh[cun[i].a])+1);
}
for(re k=l;k<j;k++) if(cun[k].pos-lsh[cun[k].a]>=0) T.clear(1,0,n,cun[k].pos-lsh[cun[k].a]);
sort(cun+mid+1,cun+r+1,com2);
solve(mid+1,r);
}
signed main()
{
freopen("sequence.in","r",stdin),freopen("sequence.out","w",stdout);
n=read();
for(re i=1;i<=n;i++) a[i]=read(),lsh[i]=a[i];
sort(lsh+1,lsh+n+1);
cnt=unique(lsh+1,lsh+n+1)-lsh-1;
for(re i=1;i<=n;i++)
{
a[i]=lower_bound(lsh+1,lsh+cnt+1,a[i])-lsh;
cun[i]=(node){a[i],i};
}
solve(1,n);
for(re i=1;i<=n;i++) ans=max(ans,f[i]);
printf("%d\n",ans);
return 0;
}