这题本来是牛客网模拟赛的题目,由于我考试的时候想歪了,想到了kruskal,结果发现做不了,于是我就把题目改了下,改成了kruskal可以做的,但是还是被验题人wjyyy用原来的方法轻轻松松搞掉了QAQ,于是我决定把题目改成输出方案2333
以上纯属扯淡
看完题目,发现题目中要求连通块最多有多少个,仔细想想发现正着推很难,那就倒着推呗。我们首先假设这棵树没有边,则当前连通块有$n$个,现在我们要在这$n$个点中加上$n-1$条边。基于连通块的定义我们可以发现:若一条边连接的两个点颜色不同,则当前的边对于目前连通块个数没有影响,若一条边连接的两个点颜色相同,则当前连通块个数需要减$1$。
所以本题做法就很清晰了:先枚举所有可以连的边(如果你稍微想一下就会发现如果枚举每个数的倍数就可以找到所有的边了),若边连接的两个点颜色不同,则若连接当前边,其对连通块个数的贡献为$1$,否则贡献为$0$,然后按照题目要求排个序,跑一边kruskal就行了。(是不是很简单,我也这么觉得QAQ)
代码如下:
#include
#define REP(i,a,b) for (register int i(a);i<=(b);i++)
namespace fast_IO {
const int IN_LEN=10000000,OUT_LEN=10000000;
char ibuf[IN_LEN],obuf[OUT_LEN],*ih=ibuf+IN_LEN,*oh=obuf;
char *lastin=ibuf+IN_LEN;
const char *lastout=ibuf+OUT_LEN-1;
inline char getchar_() {
if(ih==lastin)lastin=ibuf+fread(ibuf,1,IN_LEN,stdin),ih=ibuf;
return (*ih++);
} inline void putchar_(const char x) {
if(ih==lastout)fwrite(obuf,1,oh-obuf,stdout),oh=obuf;
*oh++=x;
} inline void flush() {
fwrite(obuf, 1, oh - obuf, stdout);
}
}
using namespace fast_IO;
using namespace std;
template
inline void Read(T&x) {
char cu=getchar();
x=0;
bool fla=0;
while(!isdigit(cu)) {
if(cu=='-')fla=1;
cu=getchar();
}
while(isdigit(cu))x=x*10+cu-'0',cu=getchar();
if(fla)x=-x;
}
template
void printe(const T x) {
if(x>=10)printe(x/10);
putchar(x%10+'0');
}
template
inline void Write(const T x) {
if(x<0)putchar('-'),printe(-x);
else printe(x);
}
//以上是快速输入输出(可以忽略)
long long a,b,n,Cnt,Num,Ans,c[5000010],Fa[5000010];
struct Node{
int x,y,v;
}k[100000010];
int Find(int x){
while(Fa[x]!=x) x=Fa[x]=Fa[Fa[x]];
return x;
}
bool Cmp(Node a,Node b){
if(a.v!=b.v){
return a.v>b.v;
}
else{
if((a.x+a.y)!=(b.x+b.y)){
return (a.x+a.y)<(b.x+b.y);
}
else{
return a.x>1) {
REP(j,2,n/i) {
k[++Cnt].x=i;
k[Cnt].y=i*j;
if(c[i]!=c[i*j]){
k[Cnt].v=1;
}
}
}
for (int i=1;i<=n;i++)
Fa[i]=i;
sort(k+1,k+Cnt+1,Cmp);
for (int i=1;i<=Cnt;i++){
a=0;
b=0;
a=Find(k[i].x);
b=Find(k[i].y);
if (a==b)
continue;
Write(k[i].x);
putchar(' ');
Write(k[i].y);
putchar('\n');
Fa[b]=a;
}
return flush(),0;//Fast IO专用结尾
}