【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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章