A

Time Limit: 3000 ms Memory Limit: 512 MB

Description

img

img


Solution

  所以说这题到底是。。怎么想到的啊啊啊啊qwq超级害怕qwq

  然而袁稳稳巨佬有优秀\(nlog^2n\)点分做法(并不会qwq),貌似是一开始预处理出每个数的因数和倍数,点分的时候用树状数组把不能走的部分去掉之类的反正很强就是了qwq

  • 10%

  枚举两个点,然后扫一遍这条路径是否合法,大概是。。\(n^3\)的?
  

  • 30%

  首先\(n^2\)预处理点\(x\)出发,是否存在一条路上没有\(x\)的因数或者倍数的路径到\(y\),存在则\(ok[x][y]=1\),否则为0,然后再\(n^2\)以每个点为根遍历,如果说一条路径上的任意一个点\(u\)都满足\(ok[x][u]=1\)\(ok[u][x]=1\)则这条路径满足条件
  

  • 50%(from yxq大佬,然而这个做法貌似场上获得了80??)

  同样是\(n^2\)以每个点为根遍历,每遍历到一个点就暴力在一个\(rec\)数组中判断这个点加入后路径是否合法,如果合法则把它的因数加进\(rec\)数组,因为因数个数是\(log\)级别的,所以是\(n^2logn\)
  

  • 100%

  首先我们可以先把所有的形如\((a,ka)\)的路径枚举出来,根据题目,一条路径不满足条件说明这条路径必定完全包含一条或多条这样的路径(下面将这种形如\((a,ka)\)的路径称为非法部分)

  直接统计答案并不是那么好处理,我们考虑将所有不合法的情况算出来,再用总的路径数减去不合法情况得到最终的答案

  记\(dfn_i\)\(i\)点的\(dfs\)序,\(ed_i\)为以\(i\)点为根的子树中\(dfs\)序最大值

  那么考虑一条完全包含非法部分\((u,v)\)的路径\((x,y)\),为了方便讨论,我们钦定\(dfn_x<dfn_y\)\(dfn_u<dfn_v\),考虑这四个点在树上的位置关系,可以分成以下两个情况:

  1.\(lca(u,v)!=u\)

    那么\(x\)\(y\)应该分别在\(u\)的子树内和\(v\)的子树内,也就是\(dfn_u<dfn_x<=ed_u\)\(dfn_v<dfn_y<=ed_v\)

​  2.\(lca(u,v)==u\)

    我们记路径\((u,v)\)中离\(u\)最近的那个点为\(g\)(就是\(u\)的一个儿子啦),那么。。应该有两种情况:

      (1)\(x\)\(u\)的祖先,\(y\)在以\(v\)为根的子树内,也就是\(dfn_x<dfn_u\)\(dfn_v<dfn_y<=ed_v\)

      (2)\(x\)在以\(v\)为根的子树内,\(y\)则是\(dfn\)序在\(ed_g\)之后的某个点,也就是\(dfn_v<dfn_x<=ed_v\)\(ed_g<dfn_y\)

  求出这个大小关系之后,答案应该就是对于每个非法部分,\(x\)\(y\)的可以选的值的数量相乘,最后再相加

  然后就发现,我们可以把上面列出来的三组不等式看成三种矩形(噢!所以到底是怎么想到的啊qwq)

  

  考虑\(x\)轴表示\(dfn_x\)\(y\)轴表示\(dfn_y\),坐标系中的一个点就表示一条完全包含非法部分的路径,那么就可以直接将上面的不等式转成矩形了,最后的答案就是这堆矩形的面积的并

  

  然而矩形面积的并怎么求呢?

  扫描线+线段树

  我们考虑将每个矩形用的两条横边(上边和下边,都是坐标系中的一段线段)来表示,我们对\(x\)轴建树,那么扫描线就可以看成一条平行于\(x\)轴的直线,从下往上扫,扫到一条下边,则这条边的区间+1,扫到一条上边,则这条边的区间-1(可见线段树中存的是这个横坐标在区间中的矩形有多少个)

  每次移动扫描线都要将ans加上(移动前的高度-移动后的高度)*线段树中根节点的\(sum\)

  比较开心的事情是,因为每次查询只需要根节点的\(sum\),所以就算是区间修改我们也不用标记下传

  

  但是注意,这里求出来的是不满足条件的路径条数qwq,所以最后要用总的路径条数减去这个面积并

  还有一点要注意,就是\(x=y\)这样的路径并没有被算成不满足条件,但是题目中这种路径确实是不满足条件的,所以我们最后还要再减去\(n\)

  

  然后就很愉快滴做完啦ovo

  代码大概长这样

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
#define TOP 20
using namespace std;
const int MAXN=1e5+10,SEG=MAXN*2;
namespace Seg{/*{{{*/
	int ch[SEG][2],sum[SEG],tag[SEG];
	int tot,n;
	void pushup(int x,int l,int r){
		sum[x]=tag[x]?(r-l+1):sum[ch[x][0]]+sum[ch[x][1]];
	}
	void _build(int x,int l,int r){
		sum[x]=0;
		if (l==r) return;
		int mid=l+r>>1;
		ch[x][0]=++tot; _build(ch[x][0],l,mid);
		ch[x][1]=++tot; _build(ch[x][1],mid+1,r);
	}
	void build(int _n){n=_n; tot=1; _build(1,1,n);}
	void _update(int x,int l,int r,int lx,int rx,int delta){
		if (l<=lx&&rx<=r){
			tag[x]+=delta;
			pushup(x,lx,rx);
			return;
		}
		int mid=lx+rx>>1;
		if (l<=mid) _update(ch[x][0],l,r,lx,mid,delta);
		if (r>mid) _update(ch[x][1],l,r,mid+1,rx,delta);
		pushup(x,lx,rx);
	}
	void update(int l,int r,int delta){_update(1,l,r,1,n,delta);}
	int query(){return sum[1];}
}/*}}}*/
struct xxx{
	int y,nxt;
}a[MAXN*2];
struct seg{
	int l,r,h,val;
	seg(){}
	seg(int _l,int _r,int _h,int _val){l=_l;r=_r;h=_h;val=_val;}
	friend bool operator < (seg x,seg y)
	{return x.h==y.h?x.l<y.l:x.h<y.h;}
}line[MAXN*TOP*2];
int dep[MAXN],h[MAXN],f[MAXN][TOP+1],dfn[MAXN],ed[MAXN];
int n,m,tot,dfn_t,cntrec;
void add(int x,int y);
void dfs(int fa,int x,int d);
int jump(int x,int d);
void Rec(int u,int v);
void solve();

int main(){
#ifndef ONLINE_JUDGE
	freopen("a.in","r",stdin);
#endif
	int x,y;
	scanf("%d",&n);
	memset(h,-1,sizeof(h));
	tot=0;
	for (int i=1;i<n;++i){
		scanf("%d%d",&x,&y);
		add(x,y); add(y,x);
	}
	dfn_t=0;
	dfs(0,1,1);
	cntrec=0;
	for (int i=1;i<=n;++i)
		for (int j=2*i;j<=n;j+=i)
			Rec(i,j);
	solve();
}

void add(int x,int y){
	a[++tot].y=y; a[tot].nxt=h[x]; h[x]=tot;
}

void dfs(int fa,int x,int d){
	int u;
	f[x][0]=fa; dep[x]=d; dfn[x]=++dfn_t;
	for (int i=1;i<=TOP;++i) f[x][i]=f[f[x][i-1]][i-1];
	for (int i=h[x];i!=-1;i=a[i].nxt){
		u=a[i].y;
		if (u==fa) continue;
		dfs(x,u,d+1);
	}
	ed[x]=dfn_t;
}

int jump(int x,int d){
	for (int i=0;i<=TOP;++i)
		if (d&(1<<i)) x=f[x][i];
	return x;
}

void Rec(int u,int v){
	if (dfn[u]>dfn[v]) swap(u,v);
	int g=jump(v,dep[v]-dep[u]-1);
	if (f[g][0]!=u){
		line[++cntrec]=seg(dfn[u],ed[u],dfn[v],1);
		line[++cntrec]=seg(dfn[u],ed[u],ed[v]+1,-1);
		return;
	}
	if (dfn[g]-1>=1){
		line[++cntrec]=seg(1,dfn[g]-1,dfn[v],1);
		line[++cntrec]=seg(1,dfn[g]-1,ed[v]+1,-1);
	}
	if (ed[g]+1<=n){
		line[++cntrec]=seg(dfn[v],ed[v],ed[g]+1,1);
		line[++cntrec]=seg(dfn[v],ed[v],n+1,-1);
	}
}

void solve(){
	sort(line+1,line+1+cntrec);
	Seg::build(n);
	ll ans=1LL*n*(n+1)/2;
	int preh=0;
	for (int i=1;i<=cntrec;){
		ans-=1LL*(line[i].h-preh)*Seg::query();
		preh=line[i].h;
		while (line[i].h==preh)
			Seg::update(line[i].l,line[i].r,line[i].val),++i;
	}
	printf("%lld\n",ans-n);
}
posted @ 2018-04-17 17:46  yoyoball  阅读(131)  评论(0编辑  收藏  举报