题目: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[2*maxn]; struct node{ ll next; ll val; ll bj; }flag[maxn]; ll da; vector<ll> mp[maxn],xx[maxn];//存下图 ll e[maxn]; ll e2[20*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=0; char c=getchar(); while(c>'9'||c<'0') c=getchar(); while(c<='9'&&c>='0') x=(x<<3)+(x<<1)+c-'0',c=getchar(); } void find(ll x,ll f){//找出重心 siz[x]=1; maxsize[x]=0; 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!=-1){ 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]=1; //printf("rt=%lld ans1=%lld\n",x,ans1); for(int i=last[x];i;i=e1[i].next){ ll v=e1[i].to; qe=0; if(vis[v]) continue; //dis[x]=q.second; get_dis(v,x,__gcd(xd[x],xd[v]),x); for(int j=0;j<qe;j++){ if(head==-1){ head=e[j]; flag[e[j]].val=1; flag[e[j]].next=-1; flag[e[j]].bj=1; } else if(flag[e[j]].bj==0){ flag[e[j]].val=1; flag[e[j]].next=head; flag[e[j]].bj=1; head=e[j]; } else{ flag[e[j]].val++; } } } ll t=head; while(t!=-1){ flag[t].bj=0; t=flag[t].next; } head=-1; //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=0; maxsize[rt]=mod; find(q,x); divide(rt); } // vis[x]=0; } void init(){ ans1=0;ans2=0; head=-1; //for(int i=0;i<=n+1;i++) mp[i].clear(); for(int i=0;i<=n+1;i++) vis[i]=0; //for(int i=0;i<=n+1;i++) flag[i]=0; for(int i=0;i<=n+1;i++) yj[i]=0; } int main(){ ll t; read(n); ll a,b,c; init(); ll xx; for(int i=1;i<=n;i++){ read(xd[i]); yj[xd[i]]++; } for(int i=1;i<=n-1;i++){ read(a); read(b); insert(a,b); } sum=n;//当前节点数 rt=0; maxsize[0]=mod;//置初值 find(1,0); divide(rt); for(int i=1;i<maxn;i++){ if(yj[i]==0) continue; cout<<i<<" "<<yj[i]<<"\n"; //printf("%I64d %I64d\n",i,yj[i]); } }