CodeForces - 990G (点分治+链表计数)

题目: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]);    
        }
} 

猜你喜欢

转载自www.cnblogs.com/Lis-/p/11370670.html