程式設計之美初賽第一場--樹

jsjliuyun發表於2014-04-19

題目2 : 樹

時間限制:4000ms
單點時限:2000ms
記憶體限制:256MB

描述

有一個N個節點的樹,其中點1是根。初始點權值都是0。

一個節點的深度定義為其父節點的深度+1,。特別的,根節點的深度定義為1。

現在需要支援一系列以下操作:給節點u的子樹中,深度在l和r之間的節點的權值(這裡的深度依然從整個樹的根節點開始計算),都加上一個數delta。

問完成所有操作後,各節點的權值是多少。


為了減少巨大輸出帶來的開銷,假設完成所有操作後,各節點的權值是answer[1..N],請你按照如下方式計算出一個Hash值(請選擇合適的資料型別,注意避免溢位的情況)。最終只需要輸出這個Hash值即可。


MOD =1000000007; // 10^9 + 7

MAGIC= 12347;

Hash =0;

For i= 1 to N do

   Hash = (Hash * MAGIC + answer[i]) mod MOD;

EndFor


輸入

第一行一個整數T (1 ≤ T ≤ 5),表示資料組數。

接下來是T組輸入資料,測試資料之間沒有空行。

每組資料格式如下:

第一行一個整數N (1 ≤ N ≤ 105),表示樹的節點總數。

接下來N - 1行,每行1個數,a (1 ≤ a ≤ N),依次表示2..N節點的父親節點的編號。

接下來一個整數Q(1 ≤ Q ≤ 105),表示操作總數。

接下來Q行,每行4個整數,u, l, r, delta (1 ≤ u ≤ N, 1 ≤ l ≤ r ≤ N, -109 ≤ delta ≤ 109),代表一次操作。


輸出

對每組資料,先輸出一行“Case x: ”,x表示是第幾組資料,然後接這組資料答案的Hash值。


資料範圍


小資料:1 ≤ N, Q ≤ 1000

大資料:1 ≤ N, Q ≤ 105


樣例解釋

點1的子樹中有1,2,3三個節點。其中深度在2-3之間的是點2和點3。

點2的子樹中有2,3兩個節點。其中沒有深度為1的節點。

所以,執行完所有操作之後,只有2,3兩點的權值增加了1。即答案是0 1 1。再計算對應的Hash值即可。




樣例輸入
1
3
1
2
2
1 2 3 1
2 1 1 1
樣例輸出
Case 1: 12348
解析:開始沒看這道題,以為這道題是什麼樹或者圖的演算法,雖然前一段時間寫了關於表示式樹,紅黑樹什麼的,但是一看那題那麼長,還有權重,這難道是什麼圖搜尋還是。。。水貨還沒練過搜尋的題,唉……最後看了看第三題--活動中心,題意挺簡單,但是沒思路哈,也怪自己平時做的題實在的太少了,扯的優點多,後來剩下了不到半小時的時間去仔細的看了下這道題,大致題意思:給出了樹的節點個數以及每個節點的父親節點,其中定義根節點的深度為1,其他節點的深度依次根據根節點來定義,然後輸入了幾組操作的命令,其實就是讓你找給定u的所有子節點,並判斷他們的深度是否在l和r之間,如果在的話則對應節點的權重+delta,最後按照題目指定的題意輸出就好了,題意大致如此,很明顯需要定義權重的陣列,節點編號的陣列,記錄每個節點父節點的陣列。
最後慌慌張張寫好了,結果時間也過了沒辦法提交驗證自己是不是對,大資料肯定是過不了,唉……竟然好多大神那麼短的時間內全部做完並且資料全過哈!膜拜大神哈!
#include <iostream>
#include <stdio.h>
#include <memory.h>
using std::endl;
using std::cin;
using std::cout;
int main()
{
	int Parent[1001];
	int Depth[1001];
	int Answer[1001];
	int T;
	cin >> T;
	int cnt=0;
	while(T--)
	{
		cnt++;
		//初始化
		memset(Parent,0,sizeof(Parent));
		memset(Depth,0,sizeof(Depth));
		memset(Answer,0,sizeof(Answer));
		//初始化根節點編號的深度值
		Depth[1]=1;
		int N;
		cin >> N;
		int n;
		//輸入2..N節點的父親節點的編號
		for(int i=2;i<=N;++i)
		{
			cin >> n;
			//儲存父親節點
			Parent[i]=n;
			//更新深度
			Depth[i]=Depth[n]+1;
		}
		int Q;
		//輸入操作的次數
		cin >> Q;
		int u, l, r, delta;
		for(int i=0;i<Q;++i)
		{
			cin >> u >> l >> r >> delta;
			for(int j=1;j<=N;++j)
			{
				//u的子樹包括u節點本身
				if(Depth[u]>=l&&Depth[u]<=r)
				{
					Answer[u]+=delta;
				}
				//如果節點的深度小於或者等於u,則不可能是u(除了u本身外)的子樹
				if(Depth[j]>Depth[u])
				{
					if(Parent[j]==u)
					{//u的第一代孩子
						if(Depth[j]>=l&&Depth[j]<=r)
						{
							Answer[j]+=delta;
						}
					}else{
						//不是u的直接孩子
						int temp=j;
						while(Depth[temp]!=Depth[u])
						{
							temp=Parent[temp];
						}
						//來判斷是否為u的子樹節點
						if(temp==u&&(Depth[j]>=l&&Depth[j]<=r))
						{
							Answer[j]+=delta;
						}
					}
				}
			}
		}
		//對儲存在Answer中的權值進行最後的輸出處理
		int MOD =1000000007; // 10^9 + 7
		int MAGIC= 12347;
		int Hash =0;
		for(int i=1;i<=N;++i)
		{
			Hash = (Hash * MAGIC + Answer[i]) % MOD;
		}
		//輸出
		cout << "Case " << cnt << ": ";
		cout << Hash << endl;
	}
	return 0;
}

相關文章