AOJ本の読書メモ   AOJ    次のAOJの問題へ    前のAOJの問題へ

CGL_6_A: Segment Intersections: Manhattan Geometry


問題へのリンク


C#のソース

using System;
using System.Collections.Generic;
using System.Linq;

// Q069 線分交差 https://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_6_A&lang=jp
class Program
{
    static string InputPattern = "InputX";

    static List<string> GetInputList()
    {
        var WillReturn = new List<string>();

        if (InputPattern == "Input1") {
            WillReturn.Add("6");
            WillReturn.Add("2 2 2 5");
            WillReturn.Add("1 3 5 3");
            WillReturn.Add("4 1 4 4");
            WillReturn.Add("5 2 7 2");
            WillReturn.Add("6 1 6 3");
            WillReturn.Add("6 5 6 7");
            //3
        }
        else {
            string wkStr;
            while ((wkStr = Console.ReadLine()) != null) WillReturn.Add(wkStr);
        }
        return WillReturn;
    }

    class XYInfoDef
    {
        internal int X1;
        internal int Y1;
        internal int X2;
        internal int Y2;
    }

    struct PointInfoDef
    {
        internal int Type; //1が下端、2が左端から右端、3が上端
        internal int X;
        internal int Y;
        internal Nullable<int> RightX;
    }

    static void Main()
    {
        List<string> InputList = GetInputList();

        int[] wkArr = { };
        Action<string> SplitAct = pStr =>
            wkArr = pStr.Split(' ').Select(pX => int.Parse(pX)).ToArray();

        var XYInfoList = new List<XYInfoDef>();
        foreach (string EachStr in InputList.Skip(1)) {
            XYInfoDef WillAdd = new XYInfoDef();
            SplitAct(EachStr);
            WillAdd.X1 = wkArr[0];
            WillAdd.Y1 = wkArr[1];
            WillAdd.X2 = wkArr[2];
            WillAdd.Y2 = wkArr[3];
            XYInfoList.Add(WillAdd);
        }

        // X座標は座標圧縮する
        var ZaatuXList = new List<int>();
        ZaatuXList.AddRange(XYInfoList.Select(pX => pX.X1));
        ZaatuXList.AddRange(XYInfoList.Select(pX => pX.X2));
        ZaatuXList = ZaatuXList.Distinct().OrderBy(pX => pX).ToList();

        // 座標圧縮後のX座標[元のX座標]なDict
        var ZaatuXInfoDict = new Dictionary<int, int>();
        for (int I = 0; I <= ZaatuXList.Count - 1; I++) {
            ZaatuXInfoDict[ZaatuXList[I]] = I;
        }

        // X座標を座標圧縮した座標に変更
        for (int I = 0; I <= XYInfoList.Count - 1; I++) {
            int BeforeX1 = XYInfoList[I].X1;
            int BeforeX2 = XYInfoList[I].X2;
            XYInfoList[I].X1 = ZaatuXInfoDict[BeforeX1];
            XYInfoList[I].X2 = ZaatuXInfoDict[BeforeX2];
        }

        // 平面走査で解く
        var PointInfoList = new List<PointInfoDef>();
        foreach (XYInfoDef EachXYInfo in XYInfoList) {
            if (EachXYInfo.X1 == EachXYInfo.X2) {
                // 下端の追加
                PointInfoDef WillAdd1;
                WillAdd1.Type = 1;
                WillAdd1.X = EachXYInfo.X1;
                WillAdd1.Y = Math.Min(EachXYInfo.Y1, EachXYInfo.Y2);
                WillAdd1.RightX = null;
                PointInfoList.Add(WillAdd1);

                // 上端の追加
                PointInfoDef WillAdd3;
                WillAdd3.Type = 3;
                WillAdd3.X = EachXYInfo.X1;
                WillAdd3.Y = Math.Max(EachXYInfo.Y1, EachXYInfo.Y2);
                WillAdd3.RightX = null;
                PointInfoList.Add(WillAdd3);
            }
            else {
                // 左端から右端の追加
                PointInfoDef WillAdd2;
                WillAdd2.Type = 2;
                WillAdd2.X = Math.Min(EachXYInfo.X1, EachXYInfo.X2);
                WillAdd2.Y = EachXYInfo.Y1;
                WillAdd2.RightX = Math.Max(EachXYInfo.X1, EachXYInfo.X2);
                PointInfoList.Add(WillAdd2);
            }
        }

        // ソートする
        PointInfoList = PointInfoList.OrderBy(pX => pX.Y).ThenBy(pX => pX.Type).ToList();

        var InsSegmentTree = new SegmentTree(ZaatuXInfoDict.Count);

        long Answer = 0;
        foreach (PointInfoDef EachPointInfo in PointInfoList) {
            // 下端の場合
            if (EachPointInfo.Type == 1) {
                InsSegmentTree.Add(EachPointInfo.X, 1);
            }

            // 左端の場合
            if (EachPointInfo.Type == 2) {
                Answer += InsSegmentTree.Query(EachPointInfo.X, (int)EachPointInfo.RightX, 0);
            }

            // 右端の場合
            if (EachPointInfo.Type == 3) {
                InsSegmentTree.Add(EachPointInfo.X, -1);
            }
        }
        Console.WriteLine(Answer);
    }
}

#region SegmentTree
// SegmentTreeクラス (RSQ and 1点加算)
internal class SegmentTree
{
    private int[] mTreeNodeArr;
    private int UB; // 木のノードの配列のUB
    private int mLeafCnt; // 葉ノードの数

    // ノードの添字を引数とし、範囲の開始添字と終了添字を持つ配列
    private struct RangeInfoDef
    {
        internal int StaInd;
        internal int EndInd;
    }
    private RangeInfoDef[] mRangeInfo;

    // コンストラクタ
    internal SegmentTree(int pLeafCnt)
    {
        // 簡単のため、葉ノード数を2のべき乗に
        int ArrLength = 0;
        for (int I = 1; I < int.MaxValue; I *= 2) {
            ArrLength += I;
            mLeafCnt = I;

            if (pLeafCnt < mLeafCnt) break;
        }

        // すべての値を0に
        UB = ArrLength - 1;
        mTreeNodeArr = new int[UB + 1];
        for (int I = 0; I <= UB; I++) {
            mTreeNodeArr[I] = 0;
        }

        // ノードの添字を引数とし、範囲の開始添字と終了添字を持つ配列の作成
        mRangeInfo = new RangeInfoDef[UB + 1];
        for (int I = 0; I <= UB; I++) {
            if (I == 0) {
                RangeInfoDef WillSet1;
                WillSet1.StaInd = 0;
                WillSet1.EndInd = mLeafCnt - 1;
                mRangeInfo[I] = WillSet1;
                continue;
            }
            int ParentNode = DeriveParentNode(I);
            RangeInfoDef ParentRangeInfo = mRangeInfo[ParentNode];

            RangeInfoDef WillSet2;
            int Mid = (ParentRangeInfo.StaInd + ParentRangeInfo.EndInd) / 2;

            if (I % 2 == 1) { // 奇数ノードの場合
                WillSet2.StaInd = ParentRangeInfo.StaInd;
                WillSet2.EndInd = Mid;
            }
            else { // 偶数ノードの場合
                WillSet2.StaInd = Mid + 1;
                WillSet2.EndInd = ParentRangeInfo.EndInd;
            }
            mRangeInfo[I] = WillSet2;
        }
        for (int I = 0; I <= UB; I++) {
            //Console.WriteLine("mRangeInfo[{0}] StaInd={1} EndInd={2}",
            //    I, mRangeInfo[I].StaInd, mRangeInfo[I].EndInd);
        }
    }

    // 親ノードの添字を取得
    private int DeriveParentNode(int pTarget)
    {
        return (pTarget - 1) / 2;
    }

    // 子ノードの添字(小さいほう)を取得
    private int DeriveChildNode(int pTarget)
    {
        return pTarget * 2 + 1;
    }

    // 葉ノードの配列の添字を木の添字に変換して返す
    private int DeriveTreeNode(int pLeafArrInd)
    {
        int BaseInd = UB - mLeafCnt + 1;
        return BaseInd + pLeafArrInd;
    }

    // 葉ノードの配列のK番目の値に、AddValを加算
    internal void Add(int pK, int pAddVal)
    {
        int CurrNode = DeriveTreeNode(pK);
        mTreeNodeArr[CurrNode] += pAddVal;

        // 登りながら更新
        while (CurrNode > 0) {
            CurrNode = DeriveParentNode(CurrNode);
            int ChildNode1 = DeriveChildNode(CurrNode);
            int ChildNode2 = ChildNode1 + 1;
            mTreeNodeArr[CurrNode] = mTreeNodeArr[ChildNode1] + mTreeNodeArr[ChildNode2];
        }
    }

    // 開始添字と終了添字とカレントノードを引数として、Sumを返す
    internal int Query(int pSearchStaInd, int pSearchEndInd, int pCurrNode)
    {
        int CurrNodeStaInd = mRangeInfo[pCurrNode].StaInd;
        int CurrNodeEndInd = mRangeInfo[pCurrNode].EndInd;

        // OverLapしてなければ、0
        if (CurrNodeEndInd < pSearchStaInd || pSearchEndInd < CurrNodeStaInd)
            return 0;

        // 完全に含んでいれば、このノードの値
        if (pSearchStaInd <= CurrNodeStaInd && CurrNodeEndInd <= pSearchEndInd)
            return mTreeNodeArr[pCurrNode];

        // そうでなければ、2つの子のSum
        int ChildNode1 = DeriveChildNode(pCurrNode);
        int ChildNode2 = ChildNode1 + 1;

        int ChildVal1 = Query(pSearchStaInd, pSearchEndInd, ChildNode1);
        int ChildVal2 = Query(pSearchStaInd, pSearchEndInd, ChildNode2);
        return ChildVal1 + ChildVal2;
    }

    internal void DebugPrint()
    {
        for (int I = 0; I <= UB; I++) {
            Console.WriteLine("mTreeNodeArr[{0}] = {1}", I, mTreeNodeArr[I]);
        }
    }
}
#endregion


解説

平面走査で解いてます。