【JZOJ 100019】【NOI2017模拟6.26】A

话说这个题号怎么这么的诡异…

Description

这里写图片描述

Solution

用点分治,统计过重心的路径个数
对于重心,以它为根,全局做一遍DFS序,
对于非重心的点x,枚举它的倍数y,有3种情况:
1. y在x到根的路径上:直接退出;
2. y在x的子树中:在y打上标记,做到y时直接退出
3. y在其他地方:y和y子树中的点都无法到达x和x的子树,x和x子树中的点都无法到达y和y的子树;

每个点的值最开始默认为1,也就是合法,
发现上面的操作只有子树赋值,所以用DFS即可
细节在这里就不一一细讲了

复杂度: O(nlog(n)2)

Code

#include <iostream>
#include <cstdio>
#include <cstdlib>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define efo(i,q) for(int i=A[q];i;i=B[i][0])
using namespace std;
typedef long long LL;
const int N=1005000;
int read(int &n)
{
    char ch=' ';int q=0,w=1;
    for(;(ch!='-')&&((ch<'0')||(ch>'9'));ch=getchar());
    if(ch=='-')w=-1,ch=getchar();
    for(;ch>='0' && ch<='9';ch=getchar())q=q*10+ch-48;n=q*w;return n;
}
int m,n;
LL ans;
int B[2*N][2],A[N],B0;
int B1[50*N][3],A1[N],B10;
struct qwqw
{
    int si,zx,TI1;
}a[N];
int b0,TI1;
struct qqww
{
    int la,s;
    int l,r,g;
}b[N*55];
int root[N];
bool z[N];
int z1[N],TI;
void link(int q,int w)
{
    B[++B0][0]=A[q];A[q]=B0,B[B0][1]=w;
    B[++B0][0]=A[w];A[w]=B0,B[B0][1]=q;
}
void CK(int &e,int g){if(b[e].g!=g)b[++b0]=b[e],b[e=b0].g=g;}
void doit(int l,int r,int e)
{
    if(b[e].la==-1)return;
    b[e].s=b[e].la*(r-l+1);
    if(l!=r)CK(b[e].l,b[e].g),CK(b[e].r,b[e].g),b[b[e].l].la=b[b[e].r].la=b[e].la;
    b[e].la=-1;
}
void build(int l,int r,int &e,int g)
{
    e=++b0;b[e]=b[0];
    b[e].g=g;b[e].la=-1;
    if(l==r)
    {
        b[e].s=1+(l==1);
        return;
    }
    int t=(l+r)>>1;
    build(l,t,b[e].l,g);
    build(t+1,r,b[e].r,g);
    b[e].s=b[b[e].l].s+b[b[e].r].s;
}
void change(int l,int r,int &e,int g,int l1,int r1,int l2)
{
    if(l1>r1)return;
    CK(e,g);
    int t=(l+r)>>1;
    doit(l,t,b[e].l),doit(t+1,r,b[e].r);
    if(l==l1&&r==r1)
    {
        b[e].la=l2;
        doit(l,r,e);
        return;
    }
    if(r1<=t)change(l,t,b[e].l,g,l1,r1,l2);
        else if(t<l1)change(t+1,r,b[e].r,g,l1,r1,l2);
        else
        {
            change(l,t,b[e].l,g,l1,t,l2);
            change(t+1,r,b[e].r,g,t+1,r1,l2);
        }
    b[e].s=b[b[e].l].s+b[b[e].r].s;
}
int find_all(int root)
{
    doit(1,n,root);
    return b[root].s;
}
int D,Ds;
int dfsf(int q,int fa,int all)
{
    a[q].zx=++b0;a[q].si=1;a[q].TI1=TI1;
    A1[q]=0;root[q]=0;
    int mx=0;
    efo(i,q)if(B[i][1]!=fa&&!z[B[i][1]])a[q].si+=dfsf(B[i][1],q,all),mx=max(mx,a[B[i][1]].si);
    mx=max(mx,all-a[q].si);
    if(mx<Ds)Ds=mx,D=q;
    return a[q].si;
}
void link1(int q,int l,int r)
{
    B1[++B10][0]=A1[q];A1[q]=B10,B1[B10][1]=l;B1[B10][2]=r;
}
void dfs1(int q,int fa,int all,int Q)
{
    if(z1[q]==1e9+TI)
        change(1,all,root[Q],Q,a[q].zx,a[q].zx+a[q].si-1,0);
    z1[q]=TI;
    fo(i,2,n/q)if(a[q*i].TI1==TI1)
    {
        if(z1[q*i]==TI)change(1,all,root[Q],Q,a[q].zx,a[q].zx+a[q].si-1,0);
        else if(a[q*i].zx<a[q].zx||a[q*i].zx>=a[q].zx+a[q].si)
        {
            link1(q*i,a[q].zx,a[q].zx+a[q].si-1);
        }else z1[i*q]=1e9+TI;
    }
    efo(i,q)if(B[i][1]!=fa&&!z[B[i][1]])dfs1(B[i][1],q,all,Q);
    z1[q]=0;
}
void dfs(int q,int fa,int all)
{
    if(z1[q]==1e9+TI)
    {
        z1[q]=0;return;
    }
    z1[q]=TI;if(!root[q])root[q]=root[fa];
    for(int i=A1[q];i;i=B1[i][0])
        change(1,all,root[q],q,B1[i][1],B1[i][2],0);
    fo(i,2,n/q)if(a[q*i].TI1==TI1)
    {
        if(z1[q*i]==TI)return;
        if(a[q*i].zx<a[q].zx||a[q*i].zx>=a[q].zx+a[q].si)
        {
            change(1,all,root[q],q,a[q*i].zx,a[q*i].zx+a[q*i].si-1,0);
        }else z1[i*q]=1e9+TI;
    }
    ans+=(LL)find_all(root[q]);
    efo(i,q)if(B[i][1]!=fa&&!z[B[i][1]])dfs(B[i][1],q,all);
    z1[q]=0;
}
void divide(int q,int all)
{
    if(all<2)return;
    Ds=D=1e9;dfsf(q,0,all);
    q=D;z[q]=1;TI1++;
    B10=b0=0;
    dfsf(q,0,all);

    b0=root[q]=0;
    build(1,all,root[q],q);
    efo(i,q)if(!z[B[i][1]])
    {
        TI++;
        dfs1(B[i][1],q,all,q);
    }
    fo(i,2,n/q)if(a[i*q].TI1==TI1)link1(i*q,1,all);
    fo(j,2,n/q)if(a[j*q].TI1==TI1)
        change(1,all,root[q],q,a[j*q].zx,a[j*q].zx+a[j*q].si-1,0);
    for(int j=A1[q];j;j=B1[j][0])
        change(1,all,root[q],q,B1[j][1],B1[j][2],0);
    int b0t=b0;
    if(q!=1)
    efo(i,q)if(!z[B[i][1]])
    {
        root[B[i][1]]=root[q];b0=b0t;
        change(1,all,root[B[i][1]],B[i][1],a[B[i][1]].zx,a[B[i][1]].zx+a[B[i][1]].si-1,0);
        TI++;
        z1[q]=TI;
        dfs(B[i][1],q,all);
    }
    efo(i,q)if(!z[B[i][1]])divide(B[i][1],a[B[i][1]].si);
}
int main()
{
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);
    int q,w;
    read(n);
    fo(i,1,n-1)read(q),read(w),link(q,w);
    divide(1,n);
    printf("%d\n",ans/2);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值