【noi.ac #937】A. t1

题目

题目描述
输入一棵 nn 个节点的树,每个点上有一个元素,元素为集合 SS 中的元素,初始为 a1,a2,…,ana1,a2,…,an。

SS 中的元素可以进行加法操作。这里,加法满足:

对于任意的两个元素,得到的结果仍然属于 SS。
满足交换律。
满足结合律。
满足幂等性(x+x=xx+x=x 对于任意的 xx 成立)。
现在输入 qq 次询问,每次输入两个节点 ss 与 tt,你需要找出对应的树上路径,设路径上的点依次为 c1,c2,…,cmc1,c2,…,cm,你需要输出 ac1+ac2+⋯+acmac1+ac2+⋯+acm。

输入格式
从标准输入读入数据。

不会输入 SS 的任何一个元素,甚至不会告诉你 SS 是什么。

第一行输入两个整数 n,qn,q。

接下来 n−1n−1 行,每行两个整数,表示树上的一条边。

接下来 qq 行,每行两个整数,表示一次询问。

输出格式
输出到标准输出。

第一行输出一个整数 kk,其中 n≤k≤2000000n≤k≤2000000,表示你扩充了 aa 数组到 kk 位。

接下来 k−nk−n 行,依次表示你对 an+1,an+2,…,akan+1,an+2,…,ak 的定义。每行输出两个整数,设你在 aiai 对应的行输出了 x,yx,y,则你需要保证 1≤x,y<i1≤x,y<i,以表示你定义 ai=ax+ayai=ax+ay。

接下来 qq 行,每行输出一个整数,依次表示你对每次询问的回答。例如输出 xx 表示你认为该次询问的答案是 axax。

你可以任意输出一种你认为正确的答案。

样例
输入
5 5
2 1
3 5
1 5
2 4
5 2
3 4
3 5
5 4
5 3
输出
17
2 1
3 5
4 2
5 1
9 6
8 6
7 9
12 11
7 5
8 6
9 15
5 7
10
13
14
16
17
子任务
测试点 n q
1 1 100
2 10 100
3 100 100
4 1000 100
5 10000 100
6 20000 1000
7 40000 10000
8 60000 100000
9 80000 100000
10 100000 100000

思路

考虑我们需要的信息:子树里最浅的一个能向上的点是谁?子树里最深的一个没被覆盖的点深度是多少?

我们记录一下fi,a,bf_{i,a,b}表示上面两个信息为aabb的时候,最少要花费多少的代价。 转移的时候枚举c,dc,d进行转移,看起来就很麻烦.

然后我们注意一个结论:考虑如果aba \geq b的话,这个bb其实是00,而如果a<ba < b的话,证明之后存在一个节点xx,到ii之后剩余能覆盖的距离至少是bb,所以aabb中一定有一个信息是没用 的. 我们令fi,af_{i,a}表示存在一个还能覆盖是aa的点,gi,bg{i,b}表示存在一个深度为bb的还没覆盖的点.考虑暴力转移,可以通过前缀和优化直接做到O(NK)O(NK)

代码

#include<bits/stdc++.h>
#define K 2000002
#define M 200002
#define N 100001
#define p 19260817
int A[p],B[K],D[K],E[K],a[N],b[M],c[M],e[N],f[N][17],g[N][17],h[N],n,q,t;long long C[K];
inline int _d(int u,int v)
{
	if(u>v)u^=v^=u^=v;
	if(!u)return v;
	long long w=(long long)u*K+v;
	for(int i=A[w%p];i;i=B[i])if(C[i]==w)return i;
	return B[++t]=A[w%p],C[A[w%p]=t]=w,D[t]=u,E[t]=v,t;
}
inline int _c(int u,int v)
{
	int w=0;
	for(int i=16;i>=0;i--)if(v>>i&1)w=_d(w,g[u][i]),u=f[u][i];
	return w;
}
inline int _b(int u,int v)
{
	if(h[u]<h[v])u^=v^=u^=v;
	for(int i=0;h[u]-h[v];i++)if(h[u]-h[v]>>i&1)u=f[u][i];
	if(u==v)return u;
	for(int i=16;i>=0;i--)if(f[u][i]==f[v][i])u=f[u][i],v=f[v][i];
	return *f[u];
}
void _a(int u)
{
	h[u]=h[*f[u]]+1,*g[u]=u;
	for(int i=1;1<<i<=h[u];i++)f[u][i]=f[f[u][i-1]][i-1],g[u][i]=++t,D[t]=g[u][i-1],E[t]=g[f[u][i-1]][i-1];
	for(int i=a[u];i;i=b[i])if(c[i]!=*f[u])*f[c[i]]=u,_a(c[i]),h[c[i]]=h[u];
}
int main()
{
	scanf("%d%d",&n,&q);
	for(int i=1,u,v;i<n;i++)scanf("%d%d",&u,&v),b[++t]=a[u],c[a[u]=t]=v,b[++t]=a[v],c[a[v]=t]=u;
	t=n,_a(1);
	for(int i=1,u,v,w,x,y;i<=q;i++)
	{
		scanf("%d%d",&u,&v),w=_b(u,v);
		if(h[u]<h[v])u^=v^=u^=v;
		e[i]=_d(_c(u,h[u]-h[w]+1),_c(v,h[v]-h[w]));
	}
	printf("%d\n",t);
	for(int i=n+1;i<=t;i++)printf("%d %d\n",D[i],E[i]);
	for(int i=1;i<=q;i++)printf("%d\n",e[i]);
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章