답안 #25872

# 제출 시각 아이디 문제 언어 결과 실행 시간 메모리
25872 2017-06-24T16:03:06 Z model_code Tapestries (POI13_gob) C++11
100 / 100
43 ms 2212 KB
/*************************************************************************
 *                                                                       *
 *                    XX Olimpiada Informatyczna                         *
 *                                                                       *
 *   Zadanie:              Gobeliny                                      *
 *   Autor:                Adam Polak                                    *
 *   Zlozonosc czasowa:    O(n^2)                                        *
 *   Opis:                 Rozwiazanie wzorcowe, liczby wymierne         *
 *                                                                       *
 *************************************************************************/


#include <cassert>
#include <cmath>
#include <iostream>
#include <string>
#include <utility>

const long long INF = 1000000000000LL;

/* Punkt lub wektor */
struct Point {

    long long x,y;

    Point() {}
    Point(long long x, long long y) {
        this->x = x;
        this->y = y;
    }

};

/* Roznica wektorow */
Point operator-(Point a, Point b) {
    return Point(a.x-b.x, a.y-b.y);
}
/* Iloczyn skalarny */
long long operator*(Point a, Point b) {
    return a.x*b.x + a.y*b.y;
}
std::pair<long long, long long> operator*(Point a, std::pair<Point, long long> b) {
    return std::make_pair(a*b.first, b.second);
}
/* Mnozenie wektora przez skalar */
Point operator*(Point a, long long k) {
    return Point(a.x*k, a.y*k);
}
/* Obrot wektora o 90 stopni CCW */
Point rot90(Point a) {
    return Point(-a.y, a.x);
}
/* Kwadrat dlugosci wektora */
long long dist2(Point a) {
    return a*a;
}
/* Iloczyn wektorowy */
long long det(Point a, Point b) {
    return a.x*b.y-a.y*b.x;
}

/* Czy kat ABC jest wypukly (kolejnosc wierzcholkow: CW) */
bool convex(Point a, Point b, Point c) {
    return det(b-a, c-b) < 0;
}

/* Czy punkt X lezy wewnatrz trojkata ABC */
bool pointInsideTriangle(Point x, Point a, Point b, Point c) {
    return convex(a,b,x) && convex(b,c,x) && convex(c,a,x);
}

/* Mnozenie dwoch long longow.
   Nie jest poprawne dla dowolnych argumentow,
   ale w tym zastosowaniu wystarcza. */
const long long BASE = (1LL<<32);
std::pair<long long, long long> mul(long long a, long long b) {
    bool minus = (a<0);
    if (minus) a = -a;
    assert( b > 0 );
    assert( (b/BASE) == 0 );
    std::pair<long long, long long> result;
    result.first = (a/BASE) * b;
    result.second = (a%BASE) * b;
    result.first += result.second/BASE;
    result.second %= BASE;
    if (minus) {
        result.first = -result.first;
        result.second = -result.second;
    }
    return result;
}

/* Porownywanie ulamkow */
bool smaller(std::pair<long long, long long> a, std::pair<long long, long long> b) {
    if (a.second < 0) {
        a.first = -a.first;
        a.second = -a.second;
    }
    if (b.second < 0) {
        b.first = -b.first;
        b.second = -b.second;
    }
    return mul(a.first, b.second) < mul(b.first, a.second);
}

/* cosinus kata pomiedzy punktami x, i1, i2
   przemnozony przez dlugosc odcinka i1i2 */
double findCos(Point i1, Point i2, Point x) {
    return det(i2-i1, x-i1) / sqrt(dist2(x-i1));
}

long long labs(long long x) {
    return x>0?x:-x;
}

/* czy punkt cand lezy 'blizej' odcinka i1 i2 niz punkt best */
bool isBetter(Point i1, Point i2, Point best, Point cand) {
    return mul(det(i2-i1,best-i1)*labs(det(i2-i1,best-i1)), dist2(cand-i1)) < mul(det(i2-i1,cand-i1)*labs(det(i2-i1,cand-i1)), dist2(best-i1));
}

/* Prosta { p \in R^2: p*normal=c } */
struct Line {

    Point normal;   /* wektor normalny */
    long long c;

    Line() {}
    /* Prosta przechodzaca przez dwa punkty */
    Line(Point a, Point b) {
        this->normal = rot90(b-a);
        this->c = this->normal*a;
    }

};

/* Czy dwie proste sa rownolegle */
bool parallel(Line a, Line b) {
    return det(a.normal, b.normal) == 0;
}

/* Przeciecie dwoch prostych (nie moga byc rownolegle) */
std::pair<Point, long long> intersect(Line a, Line b) {
    return std::make_pair(rot90(a.normal * b.c - b.normal * a.c), det(a.normal, b.normal));
}

const int MAXN = 1000;

int n;              /* Liczba wierzcholkow wielokata */
Point P[MAXN];      /* Punty na obwodzie wielokata */
bool dark[MAXN];    /* dark[i] = czy sciana miedzy p[i] a p[i+1] ma byc ciemna */

/* Czy wielokat jest gwiazdzisty */
bool isStarShaped() {
    /* Sprawdzamy, kazda prosta, ktora potencjalnie moze tworzyc krawedz jadra */
    for(int i=0; i<n; i++) {
        Line line1(P[i], P[(i+1)%n]);
        Point v = P[(i+1)%n] - P[i];
        std::pair<long long, long long> low = std::make_pair(-INF,1);
        std::pair<long long, long long> high = std::make_pair(INF,1);;
        bool ok = true;
        /* Odkladamy na tej prostej przeciecia z pozostalymi prostymi */
        for(int j=0; j<n; j++) if(i!=j) {
                Line line2(P[j], P[(j+1)%n]);
                if (parallel(line1, line2)) {
                    /* Proste rownolegle stanowia szczegolny przypadek */
                    if (line2.normal * P[i] > line2.c) {
                        ok = false;
                        break;
                    }
                } else {
                    std::pair<long long, long long> tmp = v * intersect(line1, line2);
                    if (det(line1.normal, line2.normal) > 0) {
                        if (smaller(low, tmp)) low = tmp;
                    } else {
                        if (smaller(tmp, high)) high = tmp;
                    }
                }
            }
        if (smaller(low,high) && ok) return true;
    }
    return false;
}

bool singleTest() {

    /* Wczytywanie danych */
    std::cin >> n;

    for(int i=0; i<n; i++)
        std::cin >> P[i].x >> P[i].y;

    for(int i=0; i<n; i++) {
        std::string temp;
        std::cin >> temp;
        dark[i] = temp[0]=='C';
    }

    /* Sprawdzanie, czy wszystkie sciany maja byc ciemne/jasne */
    bool allDark = true, allLight = true;
    for(int i=0; i<n; i++)
        if (dark[i])
            allLight = false;
        else
            allDark = false;

    if (allDark)
        return false;

    if (allLight)
        return isStarShaped();

    /* Wykonujemy redukcje dopoki to jest mozliwe */
    while(true) {

        /* Jesli sa trzy sciany w tym przynajmniej jedna ciemna, to sie nie da */
        if (n==3) return false;

        /* Wyszukiwanie dwoch ciemnych scian tworzacych kat wypukly */
        int i1=0,i2=0,i3=0;
        for(i1=0; i1<n; i1++) {
            i2 = (i1+1)%n;
            i3 = (i2+1)%n;
            if (dark[i1] && dark[i2] && convex(P[i1], P[i2], P[i3]))
                break;
        }
        if (i1==n) break;

        /* Wyszukiwanie wierzcholka wchodzacego najglebiej w znaleziony kat */
        int best = -1;
        for(int i=0; i<n; i++)
            if (i!=i1 && i!=i2 && i!=i3 && pointInsideTriangle(P[i], P[i1], P[i2], P[i3]))
                if ((-1) == best || isBetter(P[i1], P[i2], P[best], P[i]))
                    best = i;

        /* Jesli takiego wierzcholka nie ma, zastepujemy dwie ciemne sciany przez jedna */
        if ((-1) == best) {
            for(int i=i2+1; i<n; i++) {
                P[i-1] = P[i];
                dark[i-1] = dark[i];
            }
            n--;
            continue;
        }

        int j1 = i2, j2 = best;
        if (j2 < j1) std::swap(j1, j2);


        /* Sprawdzenie, czy po ktorejs stronie znalezionego wierzcholka maja byc same ciemne sciany */
        bool allDark1=true, allDark2=true;

        for(int i=0; i<n; i++)
            if (!dark[i]) {
                if (i>=j1 && i<j2)
                    allDark1 = false;
                else
                    allDark2 = false;
            }

        /* Jesli po obu stronach maja byc jakies jasne sciany, to sie nie da */
        if (!allDark1 && !allDark2)
            return false;

        /* "Odcinamy" te czesc wielokata, w ktorej maja byc same ciemne sciany */
        if (allDark1) {
            for(int i=j2; i<n; i++) {
                P[i-(j2-j1-1)] = P[i];
                dark[i-(j2-j1-1)] = dark[i];
            }
            dark[j1] = true;
            n -= (j2-j1-1);
        } else {
            for(int i=j1; i<=j2; i++) {
                P[i-j1] = P[i];
                dark[i-j1] = dark[i];
            }
            n = (j2-j1+1);
            dark[n-1] = true;
        }

    }

    /* Jesli dwie sasiednie sciany maja byc ciemne, to sie nie da */
    for(int i=0; i<n; i++)
        if (dark[i] && dark[(i+1)%n])
            return false;

    /*  Jesli katy po obu stronach jakiejs ciemnej sciany
        sa oba wypukle lub oba niewypukle, to sie nie da */
    for(int i=0; i<n; i++) {
        int i1 = i;
        int i2 = (i+1)%n;
        int i3 = (i+2)%n;
        int i4 = (i+3)%n;
        if (dark[i2] && convex(P[i1],P[i2],P[i3]) == convex(P[i2],P[i3],P[i4])) return false;
    }

    /* Szukamy dwoch ciemnych scian */
    int i1 = 0;
    while(!dark[i1]) i1++;
    int i2 = i1+1;
    while(i2<n && !dark[i2]) i2++;

    if (i2==n) {
        /*  Jesli jest tylko jedna ciemna sciana, odkladamy na niej
            przeciecia polplaszczyn wyznaczanych przez jasne sciany */
        Line line1(P[i1], P[(i1+1)%n]);
        Point v = P[(i1+1)%n] - P[i1];
        std::pair<long long, long long> low = std::make_pair(-INF,1);
        std::pair<long long, long long> high = std::make_pair(INF,1);;
        for(int i=0; i<n; i++) {
            if(i!=i1) {
                Line line2(P[i], P[(i+1)%n]);
                if (parallel(line1, line2)) {
                    if (line2.normal * P[i1] > line2.c)
                        return false;
                } else {
                    std::pair<long long, long long> tmp = v * intersect(line1, line2);
                    //    std::cout << "i = " << i << " tmp = " << tmp.first << ", " << tmp.second << " " << tmp.first/(long double)tmp.second << " ";
                    if (det(line1.normal, line2.normal) > 0) {
                        //      std::cout << "low\n";
                        if (smaller(low, tmp)) low = tmp;
                    } else {
                        //    std::cout << "high\n";
                        if (smaller(tmp, high)) high = tmp;
                    }
                }
            }
        }
        if (!smaller(low,high)) return false;
    } else {
        /*  Jesli sa przynajmniej dwie ciemne sciany, wyznaczamy przeciecie
            prostych na ktorych leza i sprawdzamy, czy to jest dobre miejsce
            na lampe */
        Line line1(P[i1], P[(i1+1)%n]);
        Line line2(P[i2], P[(i2+1)%n]);
        if (parallel(line1, line2)) return false;
        std::pair<Point, long long> v = intersect(line1, line2);
        for(int i=0; i<n; i++) {
            Line line(P[i], P[(i+1)%n]);
            if (dark[i] && (smaller(line.normal*v, std::make_pair(line.c,1)) || smaller(std::make_pair(line.c,1), line.normal*v))) return false;
            //  if (!dark[i] && smaller(std::make_pair(line.c,1), line.normal*v)) return false;
            if (!dark[i] && !smaller(line.normal*v, std::make_pair(line.c,1))) return false;
        }
    }

    /* Jesli do tej pory nie stwierdzilismy, ze sie nie da, to sie da */
    return true;
}

int main() {
    std::ios_base::sync_with_stdio(0);
    int tests;
    std::cin >> tests;
    while(tests--)
        if (singleTest())
            std::cout << "TAK\n";
        else
            std::cout << "NIE\n";
    return 0;
}

# 결과 실행 시간 메모리 Grader output
1 Correct 0 ms 2212 KB Output is correct
2 Correct 0 ms 2212 KB Output is correct
3 Correct 0 ms 2212 KB Output is correct
4 Correct 0 ms 2212 KB Output is correct
5 Correct 0 ms 2212 KB Output is correct
6 Correct 0 ms 2212 KB Output is correct
7 Correct 0 ms 2212 KB Output is correct
8 Correct 0 ms 2212 KB Output is correct
9 Correct 43 ms 2212 KB Output is correct
10 Correct 26 ms 2212 KB Output is correct