题目:https://vjudge.net/contest/307753#problem/J

题意:一棵树,每个点都有个权值,现在问你,树上gcd每个不同的数有多少个

思路:点分治,首先范围只有 1e5,然后我们记录一条路径的gcd,我们在重心确定后找路径,每到gcd一个数,这条路径必然是父亲节点的因子,一个数的因子不同的个数很少,其实就相当于是几个数的不同因子数,所以gcd路径不同的个数肯定很少,我们就可以在遍历子树的时候直接暴力之前出现过的路径值了,复杂度 应该是 O(n*logn*logn)的,

这题时间卡的特别紧,我本来使用map记录不同数的个数,发现超时,我认为是多了个log的原因,我就改成了链表O(1)取,但是仔细想想其实map的log其实是按节点数来的,我gcd不同的数很少,其实这个log趋近于O(1),真正的原因-输入挂(坑的死)

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<vector>
#include<queue>
#include<map>
#define maxn 200005
#define mod 0x3f3f3f3f
using namespace std;
typedef long long ll;
struct edge{
int to,next;
}e1[*maxn];
struct node{
ll next;
ll val;
ll bj;
}flag[maxn];
ll da;
vector<ll> mp[maxn],xx[maxn];//存下图
ll e[maxn];
ll e2[*maxn];
bool vis[maxn];//标记曾经使用过的重心
ll maxsize[maxn],dis[maxn],d[maxn],yj[maxn],last[maxn];//maxsize 当前节点的最大子树
ll siz[maxn],xd[maxn];// dis 到重心的距离 d 出现过的距离
ll n,m,k,rt,sum,qe,qe2,ans1,ans2,cnt,head; // siz 当前节点的子树个数 e 出现的距离 rt代表当前重心
inline void read(ll &x) {
x=; char c=getchar();
while(c>''||c<'') c=getchar();
while(c<=''&&c>='') x=(x<<)+(x<<)+c-'',c=getchar();
}
void find(ll x,ll f){//找出重心
siz[x]=;
maxsize[x]=;
for(int i=last[x];i;i=e1[i].next){
ll v=e1[i].to;
if(v==f||vis[v]) continue;//vis数组标记曾经使用过的重心
find(v,x);
siz[x]+=siz[v];
maxsize[x]=max(maxsize[x],siz[v]);
}
maxsize[x]=max(maxsize[x],sum-siz[x]);//节点总数减去当前的子树数=以当前节点为根的父亲点子树数
if(maxsize[x]<maxsize[rt]){
rt=x;
}
}
void insert(int u,int v)
{
e1[++cnt].to=v;e1[cnt].next=last[u];last[u]=cnt;
e1[++cnt].to=u;e1[cnt].next=last[v];last[v]=cnt;
}
void get_dis(ll x,ll f,ll len,ll root){ yj[len]++;
ll t=head;
while(t!=-){
yj[__gcd(t,len)]+=flag[t].val;
t=flag[t].next;
}
e[qe]=len;
qe++;
for(int i=last[x];i;i=e1[i].next){
ll v=e1[i].to;
if(v==f||vis[v]) continue;
//dis[q.first]=(dis[x]+len)%3;
get_dis(v,x,__gcd(len,xd[v]),root);
}
}
void divide(ll x){
vis[x]=;
//printf("rt=%lld ans1=%lld\n",x,ans1);
for(int i=last[x];i;i=e1[i].next){
ll v=e1[i].to;
qe=;
if(vis[v]) continue;
//dis[x]=q.second;
get_dis(v,x,__gcd(xd[x],xd[v]),x);
for(int j=;j<qe;j++){
if(head==-){
head=e[j];
flag[e[j]].val=;
flag[e[j]].next=-;
flag[e[j]].bj=;
}
else if(flag[e[j]].bj==){
flag[e[j]].val=;
flag[e[j]].next=head;
flag[e[j]].bj=;
head=e[j];
}
else{
flag[e[j]].val++;
}
}
}
ll t=head;
while(t!=-){
flag[t].bj=;
t=flag[t].next;
}
head=-;
//qe2=0;
for(int i=last[x];i;i=e1[i].next){
ll q=e1[i].to;
if(vis[q]) continue;
//if(da>0) break;
sum=siz[q];
rt=;
maxsize[rt]=mod;
find(q,x);
divide(rt);
}
// vis[x]=0;
}
void init(){
ans1=;ans2=;
head=-;
//for(int i=0;i<=n+1;i++) mp[i].clear();
for(int i=;i<=n+;i++) vis[i]=;
//for(int i=0;i<=n+1;i++) flag[i]=0;
for(int i=;i<=n+;i++) yj[i]=;
}
int main(){
ll t;
read(n);
ll a,b,c;
init();
ll xx;
for(int i=;i<=n;i++){
read(xd[i]);
yj[xd[i]]++;
}
for(int i=;i<=n-;i++){
read(a);
read(b);
insert(a,b);
}
sum=n;//当前节点数
rt=;
maxsize[]=mod;//置初值
find(,);
divide(rt);
for(int i=;i<maxn;i++){
if(yj[i]==) continue;
cout<<i<<" "<<yj[i]<<"\n";
//printf("%I64d %I64d\n",i,yj[i]);
}
}

CodeForces - 990G (点分治+链表计数)的更多相关文章

  1. Codeforces 990G 点分治+暴力

    题意:给出一棵点带权的树,求i\(\in\)[1,200000]所有路径的上点权的gcd==i的个数. 考虑点分治,对于一棵以u为根的子树,如何统计经过u的路径的答案? 显然既然是经过点u的路径,那么 ...

  2. Codeforces 293E 点分治+cdq

    Codeforces 293E 传送门:https://codeforces.com/contest/293/problem/E 题意: 给你一颗边权一开始为0的树,然后给你n-1次操作,每次给边加上 ...

  3. CodeForces 176B Word Cut (计数DP)

    Word Cut Time Limit:2000MS     Memory Limit:262144KB     64bit IO Format:%I64d & %I64u Submit St ...

  4. 51NOD 1810 连续区间 分治 区间计数

    1810 连续区间 基准时间限制:1.5 秒 空间限制:131072 KB 分值: 80     区间内所有元素排序后,任意相邻两个元素值差为1的区间称为“连续区间” 如:3,1,2是连续区间,但3, ...

  5. codeforces 161D 点分治

    传送门:https://codeforces.com/problemset/problem/161/D 题意: 求树上点对距离恰好为k的点对个数 题解: 与poj1741相似 把点分治的模板改一下即可 ...

  6. Codeforces 475D CGCDSSQ(分治)

    题意:给你一个序列a[i],对于每个询问xi,求出有多少个(l,r)对使得gcd(al,al+1...ar)=xi. 表面上是询问,其实只要处理出每个可能的gcd有多少个就好了,当左端点固定的时候,随 ...

  7. Codeforces 558E A Simple Task (计数排序&&线段树优化)

    题目链接:http://codeforces.com/contest/558/problem/E E. A Simple Task time limit per test5 seconds memor ...

  8. Codeforces 888G(分治+trie)

    按位贪心,以当前考虑位是0还是1将数分成两部分,则MST中这两部分之间只会存在一条边,因为一旦有两条或以上的边,考虑两条边在原图中所成的环,显然这两条边有一条是环上的权值最大边,不会出现在MST中.则 ...

  9. Codeforces 888G Xor-MST - 分治 - 贪心 - Trie

    题目传送门 这是一条通往vjudge的高速公路 这是一条通往Codeforces的高速公路 题目大意 给定一个$n$阶完全图,每个点有一个权值$a_{i}$,边$(i, j)$的权值是$(a_{i}\ ...

随机推荐

  1. quick BI 修改列名备注

    有些列名太长了,所以造成些影响.注意修改即可.

  2. git如何撤销工作区的修改

    对工作区中文件的修改分为三种情况: (1)还没有git add (2)已经git add (3)已经git add,并再次进行修改 对于第一种情况,直接使用git checkout -- 文件,即可撤 ...

  3. PHP批量生成底部带编号二维码(二维码生成+文字生成图片+图片拼接合并)

    PHP批量生成带底部编号二维码(二维码生成+文字生成图片+图片拼接合并) 需求: 输入编号如 : cb05-0000001  至 cb05-0000500 批量生成 以编号为名称的下图二维码,然后压缩 ...

  4. Win10.资料

    1.Win10版本consumer editions和business editions有什么区别?(http://www.winwin7.com/JC/10722.html) 2.密钥 win10 ...

  5. java创建线程的两种方式及源码解析

    创建线程的方式有很多种,下面我们就最基本的两种方式进行说明.主要先介绍使用方式,再从源码角度进行解析. 继承Thread类的方式 实现Runnable接口的方式 这两种方式是最基本的创建线程的方式,其 ...

  6. MySQL安装教程并使用springboot2和Mybatis测试

    目录 MySQL是什么 MySQL安装 开始使用一下MySQL 用spring boot2+Mybatis试试MySQL 创建数据库和表 拉通spring boot2+mybatis MySQL是什么 ...

  7. 0x3f3f3f3f 0xbfbfbfbf 等的原理及应用

    原理 0x的意思其实是十六进制,后面加的数其实就是一个十六进制数. 在十六进制中,我们知道a代表10,b代表11,c代表12,d代表13,e代表14,f代表15. 所以3f3f3f3f这个数用十进制数 ...

  8. P3773 [CTSC2017]吉夫特

    传送门 看到组合数在模 $2$ 意义下的乘积,考虑用 $lucas$ 定理把组合数拆开 $lucas$ 告诉我们,$C(n,m)$ 在模 $k$ 意义下的值,相当于 $n,m$ 在 $k$ 进制下每一 ...

  9. C#面试 笔试题 二

    1.using关键字有什么用?什么是IDisposable? using可以声明namespace的引入,还可以实现非托管资源的释放,实现了IDisposiable的类在using中创建,using结 ...

  10. 更新252板子代码(前端+cgi中间件)

    1.前端代码 前端的html.css.js代码,利用打包工具生成dist文件夹,放入lighttpd的指定目录. 2.cgi中间件 1)编译 1.下载代码工程V100R100C00 2.将工程代码以共 ...