noip模拟64

考试过程:这次考试觉得自己发挥不错,就是还是有一些没想到。
首先是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 网格图

思路:
image
说一下这个计算的过程,我们对于每一行,先枚举一个\(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;
}


原文地址:https://www.cnblogs.com/WindZR/p/15354408.html