100019. 【NOI2017模拟6.26】A

题目

大意

给出一个大小为n的树,求合法路径个数
一个路径被称为合法当且仅当其路径上没有两个数使得其中的一个数为另外一个数的倍数
n的范围为100000

题解

不妨分两种情况对每一个限制(u,v)所能造成的影响进行讨论
设dfn[u]< dfn[v],dfn[a]< dfn[b](我们设一类(a,b)因为收到了这个限制的影响不能成为合法的路径)
case 1:
v在u的子树内,不妨设g为这条路径中最接近u的点,end[v]表示在v及其子树内dfn最大的点
那么就有 dfn[a]< dfn[g] dfn[v]<=dfn[b]<=end[v] 或者
dfn[v]<=dfn[a]<=end[v] en[g]< dfn[b]
case 2:
v不在u的子树内
这种情况就比较显然了
dfn[u]<=dfn[a]<=end[u] dfn[v]<=dfn[b]<=end[v]
那么第一种情况构成了两个矩形,第二种情况构成了一个矩形
那么问题就转化成了一个二位的平面上有许多的矩形,现在要求它们的并
可以用线段树+扫描线
线段树里面可以维护3个东西,分别是:这一段有没有被一个矩形完全覆盖过,这一条线段的长度以及当前被覆盖的长度
可以发现每次在-1的时候被完全覆盖过的次数都大于1,所以每一次进行修改操作的时候我们不需要关心具体的覆盖情况,只需要维护一下根到每一个会被完全覆盖的点的覆盖情况就可以了

贴代码

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fo1(i,b,a) for(i=b;i>=a;i--)
#define ll long long

using namespace std;

const int maxn=1e5+5;

struct P{
    int c1,c2,c3,c4;
}cc[maxn*70];

struct Q{
    ll c,l,cl;
}tree[maxn*6];
int fi[maxn],ne[maxn*2],dui[maxn*2],qc[maxn];
int dfn[maxn],size[maxn],go[maxn*2],be[maxn],ed[maxn],en[maxn];
bool bz[maxn];
int i,j,k,l,n,x,y,z,now,tp,g,nc,mid,u,v,r;
ll ans,sc,pc;

void add(int x,int y){
    if (fi[x]==0) fi[x]=++now; else ne[qc[x]]=++now;
    qc[x]=now; dui[now]=y;
}
void dfs(int x){
    printf("%d %d\n",x,tp);
    dfn[x]=++tp; size[x]=1; bz[x]=true;
    int i=fi[x];
    be[x]=nc+1;
    while (i){
        if (bz[dui[i]]==true){
            i=ne[i];
            continue;
        }
        go[++nc]=dui[i];
        i=ne[i];
    }
    ed[x]=nc;
    i=fi[x];
    while (i){
        if (bz[dui[i]]==true){
            i=ne[i];
            continue;
        }
        dfs(dui[i]);
        size[x]+=size[dui[i]];
        i=ne[i];
    }
    en[x]=tp;
}
void maketree(int x,int l,int r){
    tree[x].l=r-l+1;
    if (l==r) return;
    int mid=(l+r)/2;
    maketree(x*2,l,mid);
    maketree(x*2+1,mid+1,r);
}
int ge1(){
    int x,y,z;
    if (tree[1].c) return tree[1].l; else return tree[1].cl;
}
int cmp(P x,P y){
    return x.c1<y.c1;
}
void change(int v,int l,int r,int x,int y,int cc){
    if (l==x && r==y){
        tree[v].c=tree[v].c+cc;
        if (cc==1) tree[v].cl=tree[v].l; else tree[v].cl=tree[v*2].cl+tree[v*2+1].cl;
        if (tree[v].c) tree[v].cl=tree[v].l;
    }
        else{
        int mid=(l+r)/2;
        if (y<=mid) change(v*2,l,mid,x,y,cc); else
        if (x>mid) change(v*2+1,mid+1,r,x,y,cc); else{
            change(v*2,l,mid,x,mid,cc);
            change(v*2+1,mid+1,r,mid+1,y,cc);
        }
        if (l!=r) tree[v].cl=tree[v*2].cl+tree[v*2+1].cl;
        if (tree[v].c>0) tree[v].cl=tree[v].l;
    }
}
int main(){
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);
    scanf("%d",&n);
    fo(i,1,n-1){
        scanf("%d%d",&x,&y);
        add(x,y); add(y,x);
    }
    dfs(1); tp=0;
    fo(i,1,n/2){
        j=i+i;
        while (j<=n){
            x=i; y=j;
            if (dfn[x]>dfn[y]){
                k=x; x=y; y=k;
            }
            u=x; v=y;
            if (size[x]+dfn[x]-1>=dfn[y]){
                l=be[x]; r=ed[x];
                while (r>l){
                    mid=(l+r)/2;
                    if (dfn[go[mid]]<dfn[y]) l=mid+1; else r=mid;
                }
                if (dfn[go[l]]>dfn[y]) l--;
                g=go[l];
                tp++;
                cc[tp].c1=1;
                cc[tp].c2=dfn[v];
                cc[tp].c3=en[v];
                cc[tp].c4=1;
                tp++;
                cc[tp].c1=dfn[g];
                cc[tp].c2=dfn[v];
                cc[tp].c3=en[v];
                cc[tp].c4=-1;
                tp++;
                cc[tp].c1=dfn[v];
                cc[tp].c2=en[g]+1;
                cc[tp].c3=n;
                cc[tp].c4=1;
                tp++;
                cc[tp].c1=en[v]+1;
                cc[tp].c2=en[g]+1;
                cc[tp].c3=n;
                cc[tp].c4=-1;
            } else{
                tp++;
                cc[tp].c1=dfn[u];
                cc[tp].c2=dfn[v];
                cc[tp].c3=en[v];
                cc[tp].c4=1;
                tp++;
                cc[tp].c1=en[u]+1;
                cc[tp].c2=dfn[v];
                cc[tp].c3=en[v];
                cc[tp].c4=-1;
            }
            j+=i;
        }
    }
    sort(cc+1,cc+tp+1,cmp);
    maketree(1,1,n);
    i=1;
    ans=(n*(n-1))/2;
    while (i<=tp){
        v=cc[i].c1;
        pc=(cc[i].c1-cc[i-1].c1)*ge1();
        ans-=pc;
        while (cc[i].c1==v){
            if (cc[i].c2<=cc[i].c3)
            change(1,1,n,cc[i].c2,cc[i].c3,cc[i].c4);
            i++;
        }
    }
    printf("%lld\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值