• 欢迎光临~

DRŽAVA

开发技术 开发技术 2022-07-19 次浏览

link

总感觉COCI的题面读不懂。题意是说给定一些平面内的点,点有点权,两个点连边的边权是两个点的集合距离。请求出一棵生成树,满足树内存在点权和模K为0的子集,最小化最大边的边权。

有一个很巧妙的结论,随意选出K个点一定能找出子集符合条件。于是得出结论树内结点不会超过K个,所以每个点只有最多K条边。于是考虑把每个点的前K小边找出来,从小到大排序,然后暴力加边并更新,一直更新到根有0的方案即可。

考虑为什么单纯按照 (x) 坐标排序后取一个点前后的某些点作为最近点会出现错误,显然原因是假如两个点横向靠的很近但纵向距离很远,那么两个点的实际距离是很远的,单纯按照 (y) 坐标排序也会出现相似的情况。如果我没理解错的话,第一篇题解的方法是砍去太远的点并且把所有点旋转一个角度,就像某题中的最高赞题解一样。蒟蒻有另一种奇怪的方法,是对横坐标和纵坐标先排两次序,每个点的权值记录为两次排序的位次之和,然后用新的权值再排一次序。可以被卡掉,但我并不认为出题人会可以去卡这种写法(谁能想得到呢对吧)。然后用状压和并查集维护即可。

#include<bits/stdc++.h>
//#define feyn
#define int long long 
const int N=50010;
using namespace std;
inline void read(int &wh){
    wh=0;int f=1;char w=getchar();
    while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
    while(w<='9'&&w>='0'){wh=wh*10+w-'0';w=getchar();}
    wh*=f;return;
}
inline int max(int s1,int s2){
	return s1<s2?s2:s1;
}
inline int min(int s1,int s2){
	return s1<s2?s1:s2;
}

int m,n,c[N],data[N];
struct node{int id,x,y;}a[N];
inline bool cmp1(node s1,node s2){return s1.x<s2.x;}
inline bool cmp2(node s1,node s2){return s1.y<s2.y;}
inline bool cmp3(node s1,node s2){return data[s1.id]<data[s2.id];}

inline int pp(int wh){return wh*wh;}
inline int dis(node s1,node s2){return pp(s1.x-s2.x)+pp(s1.y-s2.y);}
struct edge{
	int a,b,val;
}e[N*40];
inline bool cmpe(edge s1,edge s2){
	return s1.val<s2.val;
}
int cnt;

int f[N],p[N];
inline int find(int wh){
	return f[wh]==wh?wh:f[wh]=find(f[wh]);
}
inline void print(int wh){
	for(int i=0;i<n;i++)printf("%d ",(wh&(1<<i))>0);printf("n");
}
inline int work(int s1,int s2){
	//printf("mergingn");print(s1);print(s2);
	int an=0;
	for(int i=0;i<n;i++){
		if((s1&(1<<i))==0)continue;
		for(int j=0;j<n;j++){
			if((s2&(1<<j))==0)continue;
			an=an|(1<<(i+j)%n);
		}
	}
	return an|s1|s2;
}
inline bool merge(int s1,int s2){
	int f1=find(s1),f2=find(s2);
	if(f1==f2)return false;
	f[f2]=f1;p[f1]=work(p[f1],p[f2]);
	//printf("%lld %lldn",s1,s2);print(p[f1]);
	return p[f1]&1;
}

signed main(){
	
	#ifdef feyn
	freopen("in.txt","r",stdin);
	#endif
	
	read(m);read(n);
	for(int i=1;i<=m;i++){
		read(a[i].x);read(a[i].y);read(c[i]);
		c[i]%=n;a[i].id=i;
	}
	sort(a+1,a+m+1,cmp1);
	for(int i=1;i<=m;i++)data[a[i].id]+=i;
	sort(a+1,a+m+1,cmp2);
	for(int i=1;i<=m;i++)data[a[i].id]+=i;
	sort(a+1,a+m+1,cmp3);
	
	for(int i=1;i<=m;i++){
		for(int j=max(1,i-20);j<=min(m,i+20);j++){
			if(i==j)continue;
			e[++cnt]=(edge){a[i].id,a[j].id,dis(a[i],a[j])};
		}
	}
	sort(e+1,e+cnt+1,cmpe);
	//printf("cnt=%lldn",cnt);
	
	for(int i=1;i<=m;i++)f[i]=i,p[i]=(1<<c[i]);
	for(int i=1;i<=cnt;i++){
		if(merge(e[i].a,e[i].b)){
			printf("%.3fn",sqrt(e[i].val));
			return 0;
		}
	}
	
	return 0;
}
程序员灯塔
转载请注明原文链接:DRŽAVA
喜欢 (0)