大意
给出一个大小为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;
}