自嗨测试赛4

简单的树叶

设根节点答案为 \(x\),假设节点上操作为 \(min\),则要求相邻子节点权值都大于等于 \(x\),假设操作为 \(max\),则相邻子节点权值有 \(1\) 个大于等于 \(x\) 即可,这样我们会发现,如果一个 \(x\) 足够大,那我们必须能够按照这样的操作,在一些叶子节点中放入足够的大于等于 \(x\) 的数。

因为我们要让根节点最大,所以可以看作由根节点出发,找出所有必须大于等于 \(x\) 的叶子,给它们分别填上最大的数,只要使这些叶子的总个数 \(cnt\) 最小即可,答案就是 \(k-cnt+1\)。我们要求根节点能到达的最小叶子数,发现限制数量的是 \(max\) 操作,所以我们对于一个是 \(max\) 操作的点,从其儿子中选出一个能到达叶子数最小的即可,而 \(min\) 操作的点可以到达所有儿子的叶子。

\(f[i]\) 表示 \(i\) 最少能到达的叶子个数

\(f[i]=\sum_{j\in son(i)}f[j]\)\(i\)\(min\) 操作

\(f[i]=min_{j\in son(i)}f[j]\)\(i\)\(max\) 操作

\(f[i]=1\)\(i\) 是叶子

最终答案即为 \(k-f[1]+1\),复杂度 \(O(n)\),期望得分 \(100\)

Code
#include <cstdio>
#include <algorithm>
using namespace std;

const int maxn=3e5+10;
int n,cnt,k,a[maxn],head[maxn];
struct Edge{
	int to,nxt;
}e[maxn*2];

void add(int x,int y){
	e[++cnt]=(Edge){y,head[x]},head[x]=cnt;
}

int dfs(int x){
	if(!head[x]) return ++k,1;
	int mn=1e9,sum=0;
	for(int i=head[x];i;i=e[i].nxt){
		int tmp=dfs(e[i].to);
		mn=min(mn,tmp);
		sum+=tmp;
	}
	return a[x]?mn:sum;
}

int main(){
	freopen("leaf.in","r",stdin);
	freopen("leaf.out","w",stdout);
	scanf("%d",&n);
	for(int i=1;i<=n;++i) scanf("%d",&a[i]);
	for(int i=2;i<=n;++i){
		int x;scanf("%d",&x);
		add(x,i);
	}
	int tmp=dfs(1);
	printf("%d\n",k-tmp+1);
	return 0;
}

老姚的服装

预处理f[i][j]表示i,j作为左上角能向右下拓展的最大正方形边长,然后对于每个点i,j,可以O(1)求出以该点作为左上角能产生的最大答案是多少,假设答案为ans,我们就进行g[ans][i][j]++,最后对于每个ans,做一个二维前缀和

对于询问,我们二分一个值len,表示四分之一正方形的边长,若(x1,y1)到 \((x2-len*2+1,y2-len*2+1)\) 中有能作为左上角的,且最大答案为len的点,那么l=mid+1,否则r=mid-1

判断是否存在,因为已经对g数组后两维做了二维前缀和,直接差分判即可

Code


#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

int n,m,q,f[505][505],g[255][505][505];
char s[505][505];

int main(){
	freopen("clothes.in","r",stdin);
	freopen("clothes.out","w",stdout);
	scanf("%d%d%d",&n,&m,&q);
	for(int i=1;i<=n;++i) scanf("%s",s[i]+1);
	for(int i=n;i>=1;--i)
		for(int j=m;j>=1;--j){
			f[i][j]=1;
			if(s[i][j]==s[i+1][j]&&s[i][j]==s[i][j+1]&&s[i][j]==s[i+1][j+1])
				f[i][j]+=min(f[i+1][j],min(f[i][j+1],f[i+1][j+1]));
		}
	for(int i=1;i<=n;++i)
		for(int j=1;j<=m;++j){
			int len=f[i][j];
			if(s[i][j]!='B' || i+len*2-1>n || j+len*2-1>m) continue;
			if(f[i+len][j]!=len || s[i+len][j]!='P') continue;
			if(f[i][j+len]!=len || s[i][j+len]!='W') continue;
			if(f[i+len][j+len]<len || s[i+len][j+len]!='G') continue;
			g[len][i][j]++;
		}
	for(int i=0;i<=250;++i)
		for(int j=1;j<=n;++j)
			for(int k=1;k<=m;++k)
				g[i][j][k]+=g[i][j-1][k]+g[i][j][k-1]-g[i][j-1][k-1];
	for(int i=1;i<=q;++i){
		int x,y,xx,yy;scanf("%d%d%d%d",&x,&y,&xx,&yy);
		if(x>xx) swap(x,xx);
		if(y>yy) swap(y,yy);
		int l=1,r=min((xx-x+1)/2,(yy-y+1)/2),mid;
		while(l<=r){
			mid=(l+r)/2;
			int rx=xx-mid*2+1,ry=yy-mid*2+1;
			if(g[mid][rx][ry]-g[mid][rx][y-1]-g[mid][x-1][ry]+g[mid][x-1][y-1]) l=mid+1;
			else r=mid-1;
		}
		printf("%d\n",r*r*4);
	}
	return 0;
}

Shawk的游戏

最终答案一定可以表示成将一个主干放在第一层,其他的小分叉放在第二层。

我们就去枚举每个点作为第一层,如果周围不能放在第二层的点超过2个,那么不合法,直接输No

考虑什么样的点不能放在第二层,设枚举x在主干上,对于邻接点y

  • 度数大于3的

  • 度数为3的且除x以外任一儿子后面有分叉的

  • 度数为2的且除x以外的儿子后面有分叉的

现在我们考虑如何判分叉,一种方法是从所有的叶子开始,将所有相邻的度数小于等于2的点都删去(标记),如果有没删的点,那一定是分叉点,

然后我们对于每个点开一个数组c,c[x]为其所有相邻的点的vis标记之和,意思就是周围有几个儿子及后面没有分叉(也就是周围连了几条链)

现在我们可以判分叉了,然后对于每个点求出周围不能放第二层的点的个数,如果个数大于2,肯定不合法,否则合法

Code
#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int maxn=1e5+10;
int n,cnt,c[maxn],d[maxn],head[maxn];
bool vis[maxn];
struct Edge{
	int to,nxt;
}e[maxn*2];

void add(int x,int y){
	e[++cnt]=(Edge){y,head[x]},head[x]=cnt;
}

void del_chain(int x){
	vis[x]=1;
	for(int i=head[x];i;i=e[i].nxt)
		if(!vis[e[i].to]&&d[e[i].to]<=2) del_chain(e[i].to);
}

int main(){
	freopen("sgame.in","r",stdin);
	freopen("sgame.out","w",stdout);
	while(scanf("%d",&n)!=EOF){
		memset(head,0,sizeof(head));
		memset(vis,0,sizeof(vis));
		memset(c,0,sizeof(c));
		memset(d,0,sizeof(d));
		cnt=0;
		for(int i=1;i<n;++i){
			int x,y;scanf("%d%d",&x,&y);
			add(x,y),add(y,x);
			++d[x],++d[y];
		}
		for(int i=1;i<=n;++i)
			if(d[i]==1&&!vis[i]) del_chain(i);
		for(int x=1;x<=n;++x)
			for(int i=head[x];i;i=e[i].nxt) c[x]+=vis[e[i].to];
		bool can=1;
		for(int x=1;x<=n;++x){
			int cc=0;
			for(int i=head[x];i;i=e[i].nxt){
				int y=e[i].to;
				if(d[y]>3 || (d[y]==3&&c[y]-vis[x]<2) || (d[y]==2&&c[y]-vis[x]<1)) ++cc;
			}
			if(cc>=3){
				can=0;
				break;
			}
		}
		puts(can?"Yes":"No");
	}
	return 0;
}
posted @ 2021-03-11 15:42  liuzhaoxu  阅读(69)  评论(0编辑  收藏  举报