牛客网NOIP赛前集训营-提高组(第七场)B-随机生成树

题目描述

牛牛在纸上画了(N)个点(从(1)(N)编号),每个点的颜色用一个整数描述。

牛牛决定用这(N)个点随机生成一棵树,生成的规则如下:

  1. (1)号点是根节点
  2. 对于(2)号点到(N)号点,每个点随机指定一个父亲。(i)号点((2 leq i leq N))的父亲在i的约数中随机挑选一个。(例如(10)号点的父亲可以是(1)号,(2)号,(5)号,(7)号点的父亲只能是(1)号点)树生成完之后,牛牛可以计算出这个树有多少个联通块,一个联通块是一些点的集合,需要满足以下两个条件:
    • 从集合中任取两个点都满足:两个点颜色相同,且这两个点之间存在一条树边组成的路径,路径上的所有点都和这两个点颜色相同
    • 对于集合中的任意一个点和集合外的任意一个点:两点要么不同色,要么不存在一条树边组成的路径使得路径上所有点都和这两个点同色。

牛牛希望计算出生成的树中最多有多少个联通块

输入描述:

第一行一个整数(N)代表点数

第二行(N)个整数,第(i)个整数(c_i)代表第(i)个点的颜色

对于(30\%)的数据, (2 leq N leq 10), (颜色1 leq ext{颜色} leq 5)

对于(60\%)的数据, (2 leq N leq 5000),$颜色1 leq ext{颜色} leq $5000

对于(80\%)的数据, (2 leq N leq 200000), (颜色1 leq ext{颜色} leq 10^9)

对于(100\%)的数据, (2 leq N leq 500000), (颜色1 leq ext{颜色} leq 10^9)

输出描述:

输出一个整数最多有多少联通块

示例1

输入

4
1 1 1 2

输出

2

说明

(2)号点和(3)号点的父亲一定是(1)(4)号点父亲可能是(1)或者(2),两种情况下联通块数都是(2)

Solution

通过打暴力时候的一个操作发现解法。

我们发现,一个点的颜色只要与其父节点不同,那么联通块就会多一个,否则不变。

因此,我们只需要对于一个数,枚举其所有可行的父节点 ,只要颜色不一样就可以加。

然而这样是(O(n^2))的。

这里有一个技巧:我们化枚举因数为枚举倍数。

for(int i=1;i<=n;++i)
    for(int j=i+i;j<=n;j+=i);

这个东西的时间复杂度为(O(nlog n))

因为这个东西需要执行的次数为

[frac n1+frac n2+frac n3+...+frac nn=n(frac11+frac12+frac13+...+frac1n)=n(ln(n+1)+r) ]

其中(r)为欧拉常数,近似值为$0.5772156649 $。

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
#define lowbit(x) ((x)&(-(x)))
#define REP(i,a,n) for(register int i=(a);i<=(n);++i)
#define PER(i,a,n) for(register int i=(a);i>=(n);--i)
#define FEC(i,x) for(register int i=head[x];i;i=g[i].ne)
#define dbg(...) fprintf(stderr,__VA_ARGS__)
namespace io{
    const int SIZE=(1<<21)+1;char ibuf[SIZE],*iS,*iT,obuf[SIZE],*oS=obuf,*oT=oS+SIZE-1,c,qu[55];int f,qr;
    #define gc() (iS==iT?(iT=(iS=ibuf)+fread(ibuf,1,SIZE,stdin),(iS==iT?EOF:*iS++)):*iS++)
    inline void flush(){fwrite(obuf,1,oS-obuf,stdout);oS=obuf;}
    inline void putc(char x){*oS++=x;if(oS==oT)flush();}
    template<class I>inline void read(I &x){for(f=1,c=gc();c<'0'||c>'9';c=gc())if(c=='-')f=-1;for(x=0;c<='9'&&c>='0';c=gc())x=x*10+(c&15);x*=f;}
    template<class I>inline void write(I x){if(!x)putc('0');if(x<0)putc('-'),x=-x;while(x)qu[++qr]=x%10+'0',x/=10;while(qr)putc(qu[qr--]);}
    inline void print(const char *s){while(*s!='')putc(*s++);}
    inline void scan(char *s){for(c=gc();c<=' ';c=gc());for(;c>' ';c=gc())*(s++)=c;*s='';}
    struct Flusher_{~Flusher_(){flush();}}io_flusher_;
}//orz laofudasuan
using io::read;using io::putc;using io::write;using io::print;
typedef long long ll;typedef unsigned long long ull;
template<typename A,typename B>inline bool SMAX(A&x,const B&y){return x<y?x=y,1:0;}
template<typename A,typename B>inline bool SMIN(A&x,const B&y){return y<x?x=y,1:0;}
 
const int N=500000+7;
int n,a[N],ans=1,fst[N];
 
int main(){
#ifndef ONLINE_JUDGE
    freopen("B.in","r",stdin);freopen("B.out","w",stdout);
#endif
    read(n);for(register int i=1;i<=n;++i)read(a[i]);
    for(register int i=1;i<=n;++i)
        for(register int j=i+i;j<=n;j+=i)
            SMAX(fst[j],a[i]!=a[j]);
    for(register int i=2;i<=n;++i)ans+=fst[i];
    write(ans),putc('
');
}
原文地址:https://www.cnblogs.com/hankeke/p/nowcoder-noiptg7-B.html