给定一棵大小为 n n n的树,规定编号为 i i i的点不能去到编号处于 [ i − k , i + k ] [i-k,i+k] [i−k,i+k]之间, 求所有满足条件的路径数
数据范围: n ≤ 3 × 1 0 6 , k ≤ 10 n\leq 3\times 10^6,k\leq 10 n≤3×106,k≤10
我们发现 k k k很小,所以可以把所有的限制点对都求出来,然后就变成了树这道题了
这里再讲一遍,假如规定 x x x不能到 y y y
第二种情况蓝色区域的求法:求出 y y y的祖宗中,深度最小的 x x x的儿子,换言之就是 y y y所在这条链上 x x x的儿子,我们记为 z z z,则蓝色区域即为 d f s dfs dfs序 1 ∼ d f n [ z ] − 1 1\sim dfn[z]-1 1∼dfn[z]−1的所有节点
这样我们就得到了若干组容斥关系,如果有一个容斥关系 ( l 1 , r 1 ) (l_1,r_1) (l1,r1)不能去到 ( l 2 , r 2 ) (l_2,r_2) (l2,r2)
这说明对于任意的 x ∈ [ l 1 , r 1 ] x\in[l_1,r_1] x∈[l1,r1]都不能去到 y ∈ [ l 2 , r 2 ] y\in[l_2,r_2] y∈[l2,r2],如果我们把这个 x , y x,y x,y分别看成横纵坐标则
任意一个横坐标为 ( l 1 ∼ r 1 ) (l_1\sim r_1) (l1∼r1),纵坐标为 ( l 2 ∼ r 2 ) (l_2\sim r_2) (l2∼r2)都是不合法的点
注意到这些点恰好围成了一个矩形,左下角坐标为 ( l 1 , l 2 ) (l_1,l_2) (l1,l2),右上角坐标为 ( r 1 , r n ) (r_1,r_n) (r1,rn)
而且不合法的整点(横纵坐标均为整数的点)的个数恰好就是这个矩形的面积
于是,对于所有的限制条件,我们都可以把它看做一个矩形,而不合法的个数,就刚好是矩形的面积并 r e s res res
求出了不合法的个数,用总路径数减去不合法的个数即为答案,即 C n 2 + n − r e s C_{n}^2+n-res Cn2+n−res( + n +n +n是因为在该题中,自己到自己也算一条路径)
扫描线+线段树就可以解决这个问题,不熟悉这个算法的人可以自行去洛谷搜模板或者做一下Atlantis
时间复杂度: O ( n k l o g ( n k ) ) O(nk\ log\ ( nk)) O(nk log (nk))
**#pragma GCC optimize(2)
#include
#include
#include
#include
#define N 3000010
using namespace std;int n,k,cnt,dfn[N],rfn[N],ed[N],dep[N],f[N][21];
inline long long read()
{
long long f=0,d=1;char c;
while(c=getchar(),!isdigit(c)) if(c=='-') d=-1;f=(f<<3)+(f<<1)+c-48;
while(c=getchar(),isdigit(c)) f=(f<<3)+(f<<1)+c-48;
return d*f;
}
struct xds//线段树
{
#define lson k<<1
#define rson k<<1|1
int dat[N<<2],lzy[N<<2];
inline void up(int k,int l,int r)
{
if(lzy[k]) dat[k]=r-l+1;
else if(l==r) dat[k]=0;else
dat[k]=dat[lson]+dat[rson];
return;
}
inline void modify(int ql,int qr,int d,int k=1,int l=1,int r=n)
{
if(ql<=l&&r<=qr)
{
lzy[k]+=d;
up(k,l,r);
return;
}
int mid=l+r>>1;
if(ql<=mid) modify(ql,qr,d,lson,l,mid);
if(qr>mid) modify(ql,qr,d,rson,mid+1,r);
up(k,l,r);
return;
}
#undef lson
#undef rson
}T;
struct tl//树的部分,同时用倍增来求蓝色区域
{
int l[N],tot;
struct node
{
int next,to;
}e[N<<1];
inline void add(int u,int v){e[++tot]=(node){l[u],v};l[u]=tot;return;}
inline void dfs(int x)
{
dfn[++cnt]=x;rfn[x]=cnt;
for(register int i=l[x];i;i=e[i].next)
{
int y=e[i].to;
if(rfn[y]) continue;
f[y][0]=x;dep[y]=dep[x]+1;
dfs(y);
}
ed[x]=cnt;
return;
}
inline int LCA(int x,int y)
{
for(register int i=20;i>=0;i--) if(dep[f[y][i]]>dep[x]) y=f[y][i];
return y;
}
}GT;
struct node{int lie,l,r,d;}sec[N<<2];
int tot;
inline void add(int x1,int x2,int y1,int y2)
{
sec[++tot]=(node){x1,y1,y2,1};
sec[++tot]=(node){x2+1,y1,y2,-1};
return;
}
inline bool cmp(node x,node y){return x.lie<y.lie;}
long long ans;
inline void Ban(int x,int y)//表示x不能到y的处理
{
if(rfn[x]>rfn[y]) swap(x,y);
if(rfn[y]<=ed[x]&&rfn[y]>rfn[x])
{
int son=GT.LCA(x,y);
if(rfn[son]>1) add(1,rfn[son]-1,rfn[y],ed[y]);
if(ed[son]<n) add(rfn[y],ed[y],ed[son]+1,n);
}
else add(rfn[x],ed[x],rfn[y],ed[y]);
}
signed main()
{
int size = 256 << 20; //250M
char*p=(char*)malloc(size) + size;
__asm__("movl %0, %%esp\n" :: "r"(p) );
n=read();k=read();
for(register int i=1,x,y;i<n;i++) x=read(),y=read(),GT.add(x,y),GT.add(y,x);
GT.dfs(1);
for(register int j=1;j<=20;j++)
for(register int i=1;i<=n;i++)
f[i][j]=f[f[i][j-1]][j-1];
for(register int i=1;i<=n;i++)
for(register int j=i+1;j<=i+k;j++)
Ban(i,j);
sort(sec+1,sec+1+tot,cmp);
for(register int i=1,j=1;i<=n;i++)
{
for(;j<=tot&&sec[j].lie<=i;j++)
T.modify(sec[j].l,sec[j].r,sec[j].d);
ans+=T.dat[1];
}
printf("%lld\n",(long long)n*(n-1)/2+n-ans);
}