【类并查集-思维】NOI.AC——CSP2019模拟 Day 5——圆

前言

发现自己又“独树一帜”了一回,和大家的思路都不同...可惜没调出来,挖坑以后再调吧...

题目

本题下发文件下载

分析

(直接看正解见横线以下)

自己的思路(没调过):

如果要构成一个正K边形,在圆上肯定要有K个相等的大线段,

设这K个相等的大线段长度为len,len = C / k

这些大线段又由许多初始点构成的小线段构成

所以对于圆上的每个小线段d[ i ],有以下三种情况:(设已有的大线段数量为cnt,已访问过的线段为tot)

1.d[ i ]=len:长度直接符合,cnt++

2.d[ i ]>len:因为小线段不能拆分,于是直接无解,print “No”

3.d[ i ]<len:要和其他线段(长度也小于len)合起来,使得长度等于len,就要看是和其左边的线段合起来,还是和右边的线段合起来

最后如果tot=n且cnt=k,则“Yes”,否则“No”

我是用的搜索实现的,不过很遗憾,只有30分,错因不明,只好学习别人的做法了


后来去问了一下某Tao大佬,好像说我没有考虑和左右线段一起合起来的情况...


AC代码思路:

形成K边形时,选中的K个点肯定相距都为len = C / k,所以对于每个点,找到和它相距len的点,

如果同属于一个并查集就“Yes”,否则就加入一个并查集,没有解就“No”

在一个并查集就相当于它们都属于构成某K边形的点or边的集合

PS.我也不知道我理解对了没有QAQ!

官方题解:

玄学搜索30分代码

#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN=1e5;
bool vis[MAXN+5];
int a[MAXN+5],d[2*MAXN+5];
int n,c,k;
int len;
void dfs(int id,int cnt,int tot)
{
	tot++,vis[id]=1;
	if(d[id]==len)
		cnt++;
	//printf("id:%d cnt:%d tot:%d d:%d\n",id,cnt,tot,d[id]);
	if(cnt==k&&tot==n)
	{
		printf("Yes\n");
		exit(0);
	}
	if(tot==n)
		return ;
	if(d[id]==len)
	{
		int x=id+1,y=id-1;
		if(x==n+1) x=1;
		if(y==0) y=n;
		if(!vis[x])
			dfs(x,cnt,tot);
		if(!vis[y])
			dfs(y,cnt,tot);
	}
	if(d[id]>len)
	{
		printf("No\n");
		exit(0);
	}
	if(d[id]<len)
	{
		int x=id+1,y=id-1;
		if(x==n+1) x=1;
		if(y==0) y=n;
		if(!vis[x]&&d[x]+d[id]<=len)
		{
			d[x]+=d[id];
			dfs(x,cnt,tot);
			d[x]-=d[id];
		}
		if(!vis[y]&&d[y]+d[id]<=len)
		{
			d[y]+=d[id];
			dfs(y,cnt,tot);
			d[y]-=d[id];
		}
	}
	vis[id]=0;
}
int main()
{
	scanf("%d%d%d",&n,&c,&k);
	len=c/k;
	for(int i=2;i<=n;i++)
	{
		scanf("%d",&a[i]);
		d[i-1]=a[i]-a[i-1];
	}
	d[n]=c-a[n];
	dfs(1,0,0);
	printf("No\n");
	return 0;
}

AC代码

#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN=1e5;
int a[MAXN+5],fa[MAXN+5];
int n,c,k,len;
int Find(int x)
{
	return x==fa[x]?x:fa[x]=Find(fa[x]);
}
int main()
{
	scanf("%d%d%d",&n,&c,&k);
	len=c/k;
	for(int i=2;i<=n;i++)
		scanf("%d",&a[i]);
	for(int i=1;i<=n;i++)
		fa[i]=i;
	for(int i=1;i<=n;i++)
	{
		int j=lower_bound(a+1,a+n+1,(a[i]+len)%c)-a;
		if(j<=n&&a[j]==(a[i]+len)%c)
		{
			if(Find(i)==Find(j))
			{
				printf("Yes\n");
				return 0;
			}
			fa[Find(i)]=Find(j);		
		}
	}
	printf("No\n");
	return 0;
}

总结

我始终觉得自己的思路是可行的...只是不知道实现时哪里出了问题...qwq.../执着

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值