NOIP模拟65

T1 网格图

解题思路

60pts 就是个zz做法。。(我考场上造了一个 \(500\times 500\)X,一看挺快,就以为 \(n^4\) 可以切,然而。。)

正解有一点难度,对于每一个节点维护所在的联通块及其大小,发现对于每一次所扫描的方块右移所变化的只有两列,于是我们只需要维护,这两列的变化就好了。

然后再扫描矩形的四个相邻的边,计算所相连的联通块,计算答案就好了。

code

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Failed"<<endl
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const int N=510;
int n,m,ans,cnt,all,s[N][N],fa[N*N],siz[N*N];
int d1[10]={0,0,0,1,-1},d2[10]={0,1,-1,0,0};
vector<int> v;
char ch[N];
int id(int x,int y){return (x-1)*n+y;}
int find(int x)
{
	if(fa[x]==x) return x;
	return fa[x]=find(fa[x]);
}
void pre_work(int x,int y)
{
	cnt=0; vector<int>().swap(v);
	for(int i=x;i<=x+m-1;i++)
		for(int j=y;j<=y+m-1;j++)
			if(s[i][j])
				siz[find(id(i,j))]--;
	for(int i=1;i<=m;i++)
	{
		if(x>1&&s[x-1][i]) v.push_back(find(id(x-1,i)));
		if(x+m<=n&&s[x+m][i]) v.push_back(find(id(x+m,i)));
		if(s[x+i-1][m+1]) v.push_back(find(id(x+i-1,m+1)));
	}
	sort(v.begin(),v.end()); v.erase(unique(v.begin(),v.end()),v.end());
	for(int i=0;i<v.size();i++) cnt+=siz[v[i]]; ans=max(ans,m*m+cnt);
}
void work(int x,int y)
{
	for(int i=1;i<=m;i++)
	{
		if(s[x+i-1][y-1]) siz[find(id(x+i-1,y-1))]++;
		if(s[x+i-1][y+m-1]) siz[find(id(x+i-1,y+m-1))]--;
	}
	vector<int>().swap(v); cnt=0;
	for(int i=1;i<=m;i++)
	{
		if(x>1&&s[x-1][y+i-1]) v.push_back(find(id(x-1,y+i-1)));
		if(y>1&&s[x+i-1][y-1]) v.push_back(find(id(x+i-1,y-1)));
		if(x+m<=n&&s[x+m][y+i-1]) v.push_back(find(id(x+m,y+i-1)));
		if(y+m<=n&&s[x+i-1][y+m]) v.push_back(find(id(x+i-1,y+m)));
	}
	sort(v.begin(),v.end()); v.erase(unique(v.begin(),v.end()),v.end());
	for(int i=0;i<v.size();i++) cnt+=siz[v[i]]; ans=max(ans,m*m+cnt);
}
void end_work(int x,int y)
{
	for(int i=x;i<=x+m-1;i++)
		for(int j=y;j<=y+m-1;j++)
			if(s[i][j])
				siz[find(id(i,j))]++;
}
signed main()
{
	freopen("grid.in","r",stdin); freopen("grid.out","w",stdout);
	n=read(); m=read(); if(m>=n) printf("%lld",n*n),exit(0);
	for(int i=1;i<=n;i++)
	{
		scanf("%s",ch+1);
		for(int j=1;j<=n;j++)
			siz[++all]=s[i][j]=(ch[j]=='.');
	}
	for(int i=1;i<=all;i++) fa[i]=i;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			if(s[i][j])
				for(int k=1;k<=4;k++)
				{
					int x=i+d1[k],y=j+d2[k];
					if(x<1||x>n||y<1||y>n) continue;
					if(find(id(i,j))==find(id(x,y))||!s[x][y]) continue;
					siz[find(id(x,y))]+=siz[find(id(i,j))];
					fa[find(id(i,j))]=find(id(x,y));
				}
	for(int i=1;i<=n-m+1;i++)
	{
		pre_work(i,1);
		for(int j=2;j<=n-m+1;j++) work(i,j);
		end_work(i,n-m+1);
	}
	printf("%lld",ans);
	return 0;
}

T2 序列问题

解题思路

CDQ 分治优化 DP 板子题。。

可是我居然想了三种 DP 方程,然而前两种根本无法优化。。(1,2)

然后就有了现在的第三种 DP 状态可以由 \(j\) 转移到 \(i\) 当且仅当 \(i>j,s_i>s_i,s_i-i\le s_j-j\)

就是三维偏序吗,我就用了一个维护后缀的树状数组,一开始是编号有顺序,CDQ 处理掉 s 的大小关系,然后树状数组维护最后一个条件。

code

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Failed"<<endl
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const int N=5e5+10,M=1e3+10,INF=1e18;
int n,ans,all,cnt,lsh[N],f[N];
struct Node{int id,num,dat;}s[N];
bool comp1(Node x,Node y){if(x.num!=y.num) return x.num<y.num; return x.dat<y.dat;}
bool comp2(Node x,Node y){return x.id<y.id;}
struct BIT
{
	int tre[N];
	int lowbit(int x){return x&(-x);}
	void clear(int x){for(int i=x;i;i-=lowbit(i))tre[i]=0;}
	void insert(int x,int val){for(int i=x;i;i-=lowbit(i))tre[i]=max(tre[i],val);}
	int query(int x){int maxn=0;for(int i=x;i<=cnt;i+=lowbit(i))maxn=max(maxn,tre[i]);return maxn;}
}T;
void CDQ(int l,int r)
{
	if(l==r) return f[l]=max(f[l],(s[l].num<=n)*1ll),void();
	int mid=(l+r)>>1,i=l,j=mid+1; CDQ(l,mid);
	sort(s+l,s+mid+1,comp1); sort(s+mid+1,s+r+1,comp1);
	while(j<=r)
		if(i>mid||s[j].num<=s[i].num)
		{
			if(s[j].num>s[i-1].num) f[s[j].id]=max(f[s[j].id],T.query(s[j].dat)+1);
			j++;
		}
		else
		{
			if(s[i].num<=s[i].id) T.insert(s[i].dat,f[s[i].id]);
			i++;
		}
	for(i=l;i<=mid;i++) if(s[i].num<=s[i].id) T.clear(s[i].dat);
	sort(s+mid+1,s+r+1,comp2);
	CDQ(mid+1,r);
}
signed main()
{
	freopen("sequence.in","r",stdin); freopen("sequence.out","w",stdout);
	n=read(); for(int i=1;i<=n;i++) s[i].num=read(),lsh[++cnt]=s[i].num-i,s[i].id=i;
	sort(lsh+1,lsh+cnt+1); cnt=unique(lsh+1,lsh+cnt+1)-lsh-1;
	for(int i=1;i<=n;i++) s[i].dat=lower_bound(lsh+1,lsh+cnt+1,s[i].num-i)-lsh;
	CDQ(1,n); for(int i=1;i<=n;i++) ans=max(ans,f[i]);
	printf("%lld",ans);
	return 0;
}

T3 置换

大坑未补

T4 同桌的你

大坑未补

posted @ 2021-10-01 20:24  Varuxn  阅读(68)  评论(0编辑  收藏  举报