SRM470 Div1 Medium(500) DrawingLines

DrawingLines

既に引かれている線分同士の交点、既に引かれている線分とこれから引く線分の交点、これから引く線分同士の交点に分けて考える。

既に引かれている線分の本数をmとする。
既に引かれている線分同士の交点数はO(m2)で求められる。
これから引く線分同士の交点数の期待値は、任意の2点から引く線分が交差する確率は0.5なので、(n-m)*(n-m-1)/2*0.5
既に引かれている線分とこれから引く線分の交点数の期待値。
既に引かれているある線分1本について、この線分の始点より左にある未使用の点数をsl、右をsr、終点より左をel、右をerとすると、この線分と交点を作る線分は(sl*er+sr*el)通り考えられる。それぞれの線分が実際に引かれる確率は1/(n-m)。


MemberSRM470 Div1 500 DrawingLines - 徒然とコード書き

え、O(n2|startDot|)って通るの? n2を見て、即座にチャレンジしてた((((((;゚Д゚))))))

#include <vector>

using namespace std;

class DrawingLines
{
public:
    double countLineCrossings( int n, vector <int> startDot, vector <int> endDot );
};

double DrawingLines::countLineCrossings( int n, vector <int> startDot, vector <int> endDot )
{
    double p = 0;

    int m = (int)startDot.size();

    //  既に引かれている線分同士の交点数
    for ( int i=0; i<m; i++ )
        for ( int j=i+1; j<m; j++ )
            if ( (startDot[i]-startDot[j])*(endDot[i]-endDot[j]) < 0 )
                p += 1;

    //  既に引かれている線分と、これから引く線分の交点数の期待値
    if ( n - m > 0 )
    for ( int i=0; i<m; i++ )
    {
        int sl = startDot[i] - 1;   //  始点より左にある未使用の点数
        int sr = n - startDot[i];   //  始点より右にある未使用の点数
        int el = endDot[i] - 1;     //  終点より左にある未使用の点数
        int er = n - endDot[i];     //  終点より左にある未使用の点数

        for ( int j=0; j<m; j++ )
        {
            if ( startDot[j] < startDot[i] )  sl--;
            if ( startDot[j] > startDot[i] )  sr--;

            if ( endDot[j] < endDot[i] )  el--;
            if ( endDot[j] > endDot[i] )  er--;
        }

        p += (double)(sl*er+sr*el) / (n-m);
    }

    //  これから引く線分同士の交点数の期待値
    p += (double)(n-m) * (n-m-1) / 4;

    return p;
}