AtCoderのABC
次のABCの問題へ
前のABCの問題へ
ABC202-E Count Descendants
C#のソース
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static string InputPattern = "InputX";
static List<string> GetInputList()
{
var WillReturn = new List<string>();
if (InputPattern == "Input1") {
WillReturn.Add("7");
WillReturn.Add("1 1 2 2 4 2");
WillReturn.Add("4");
WillReturn.Add("1 2");
WillReturn.Add("7 2");
WillReturn.Add("4 1");
WillReturn.Add("5 5");
//3
//1
//0
//0
}
else {
string wkStr;
while ((wkStr = Console.ReadLine()) != null) WillReturn.Add(wkStr);
}
return WillReturn;
}
// 隣接リスト
static Dictionary<int, List<int>> mToNodeListDict = new Dictionary<int, List<int>>();
// ノードごとに部分木の訪問前後の、指定レベルのノード数を保存
class QueryAnswerInfoDef
{
internal int QueryNo;
internal int D;
internal int BeforeCnt;
internal int AfterCnt;
}
static Dictionary<int, List<QueryAnswerInfoDef>> mQueryAnswerListDict =
new Dictionary<int, List<QueryAnswerInfoDef>>();
static void Main()
{
List<string> InputList = GetInputList();
int[] pArr = InputList[1].Split(' ').Select(pX => int.Parse(pX)).ToArray();
for (int I = 0; I <= pArr.GetUpperBound(0); I++) {
int FromNode = I + 2;
int ToNode = pArr[I];
if (mToNodeListDict.ContainsKey(FromNode) == false) {
mToNodeListDict[FromNode] = new List<int>();
}
mToNodeListDict[FromNode].Add(ToNode);
if (mToNodeListDict.ContainsKey(ToNode) == false) {
mToNodeListDict[ToNode] = new List<int>();
}
mToNodeListDict[ToNode].Add(FromNode);
}
int[] wkArr = { };
Action<string> SplitAct = pStr =>
wkArr = pStr.Split(' ').Select(pX => int.Parse(pX)).ToArray();
int QueryNo = 0;
foreach (string EachStr in InputList.Skip(3)) {
SplitAct(EachStr);
int U = wkArr[0];
int D = wkArr[1];
if (mQueryAnswerListDict.ContainsKey(U) == false) {
mQueryAnswerListDict[U] = new List<QueryAnswerInfoDef>();
}
var WillAdd = new QueryAnswerInfoDef();
WillAdd.QueryNo = QueryNo++;
WillAdd.D = D;
WillAdd.BeforeCnt = 0;
WillAdd.AfterCnt = 0;
mQueryAnswerListDict[U].Add(WillAdd);
}
Solve();
}
static void Solve()
{
ExecEulerTour();
// 件数の度数分布表
int UB = mEulerTourJyoutaiList.Max(pX => pX.Level);
int[] CntArr = new int[UB + 1];
foreach (JyoutaiDef EachJyoutai in mEulerTourJyoutaiList) {
// ノードごとに部分木の訪問前後の、指定レベルのノード数を保存
if (EachJyoutai.IsStart) {
if (mQueryAnswerListDict.ContainsKey(EachJyoutai.Node)) {
List<QueryAnswerInfoDef> ListP = mQueryAnswerListDict[EachJyoutai.Node];
for (int I = 0; I <= ListP.Count - 1; I++) {
int TargetDepth = ListP[I].D;
if (TargetDepth <= CntArr.GetUpperBound(0)) {
ListP[I].BeforeCnt = CntArr[TargetDepth];
}
}
}
}
if (EachJyoutai.IsStart) {
CntArr[EachJyoutai.Level]++;
}
if (EachJyoutai.IsEnd) {
if (mQueryAnswerListDict.ContainsKey(EachJyoutai.Node)) {
List<QueryAnswerInfoDef> ListP = mQueryAnswerListDict[EachJyoutai.Node];
for (int I = 0; I <= ListP.Count - 1; I++) {
int TargetDepth = ListP[I].D;
if (TargetDepth <= CntArr.GetUpperBound(0)) {
ListP[I].AfterCnt = CntArr[TargetDepth];
}
}
}
}
}
// クエリに回答
var QueryAnswerInfoList = new List<QueryAnswerInfoDef>();
foreach (List<QueryAnswerInfoDef> EachList in mQueryAnswerListDict.Values) {
QueryAnswerInfoList.AddRange(EachList);
}
var sb = new System.Text.StringBuilder();
foreach (var Item in QueryAnswerInfoList.OrderBy(pX => pX.QueryNo)) {
sb.Append(Item.AfterCnt - Item.BeforeCnt);
sb.AppendLine();
}
Console.Write(sb.ToString());
}
struct JyoutaiDef
{
internal int Node;
internal int Level;
internal bool IsStart;
internal bool IsEnd;
}
// オイラーツアーを行い、下記の2通りのタイミングでノードをAddする
// 1 最初の訪問時
// 2 子の部分木を訪問完了時
static List<JyoutaiDef> mEulerTourJyoutaiList = new List<JyoutaiDef>();
static void ExecEulerTour()
{
var Stk = new Stack<JyoutaiDef>();
JyoutaiDef WillPush;
WillPush.Node = 1;
WillPush.Level = 0;
WillPush.IsStart = false; WillPush.IsEnd = true;
Stk.Push(WillPush);
WillPush.IsStart = true; WillPush.IsEnd = false;
Stk.Push(WillPush);
var VisitedSet = new HashSet<int>();
VisitedSet.Add(1);
while (Stk.Count > 0) {
JyoutaiDef Popped = Stk.Pop();
mEulerTourJyoutaiList.Add(Popped);
if (Popped.IsEnd) {
continue;
}
if (mToNodeListDict.ContainsKey(Popped.Node)) {
foreach (int EachToNode in mToNodeListDict[Popped.Node]) {
if (VisitedSet.Add(EachToNode)) {
WillPush.Node = EachToNode;
WillPush.Level = Popped.Level + 1;
WillPush.IsStart = false; WillPush.IsEnd = true;
Stk.Push(WillPush);
WillPush.IsStart = true; WillPush.IsEnd = false;
Stk.Push(WillPush);
}
}
}
}
}
}
解説
再帰でオイラツアーをしたら、スタックオーバフローになったので
Stackクラスで、
行きがけ用と、帰りがけ用で
2回PushするDFSでオイラーツアーを行ってます。
クエリを先読みしておいて、部分木の高さが必要なノードであれば
部分木の訪問前後で指定レベルのノード数を保存してます。