Compare commits
2 commits
c762cafa58
...
27db1f5e06
Author | SHA1 | Date | |
---|---|---|---|
27db1f5e06 | |||
b944df81ba |
18 changed files with 1432 additions and 0 deletions
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
polyup
|
||||
url
|
||||
url_internal
|
||||
*.pdf
|
||||
*.out
|
6
algo/mst_oriented/01.in
Normal file
6
algo/mst_oriented/01.in
Normal file
|
@ -0,0 +1,6 @@
|
|||
5 5 0
|
||||
0 1 5
|
||||
0 2 3
|
||||
2 3 2
|
||||
3 4 2
|
||||
4 2 2
|
12
algo/mst_oriented/02.in
Normal file
12
algo/mst_oriented/02.in
Normal file
|
@ -0,0 +1,12 @@
|
|||
9 11 0
|
||||
0 1 4
|
||||
2 6 4
|
||||
1 2 3
|
||||
2 3 3
|
||||
3 4 3
|
||||
4 5 3
|
||||
5 1 3
|
||||
4 7 1
|
||||
7 5 1
|
||||
5 4 1
|
||||
7 8 2
|
202
algo/mst_oriented/mst_oriented.cpp
Normal file
202
algo/mst_oriented/mst_oriented.cpp
Normal file
|
@ -0,0 +1,202 @@
|
|||
#include <bits/stdc++.h>
|
||||
|
||||
#define FOR(i, n) for(lli i = 0; i < (lli)(n); ++i)
|
||||
|
||||
#define X(A) get<0>(A)
|
||||
#define Y(A) get<1>(A)
|
||||
#define Z(A) get<2>(A)
|
||||
#define W(A) get<3>(A)
|
||||
|
||||
#define mt make_tuple
|
||||
|
||||
using namespace std;
|
||||
using lli = long long int;
|
||||
|
||||
using pii = tuple<lli, lli>;
|
||||
using piii = tuple<lli, lli, lli>;
|
||||
using vi = vector<lli>;
|
||||
using vii = vector<pii>;
|
||||
using viii = vector<piii>;
|
||||
using vvi = vector<vi>;
|
||||
using vvii = vector<vii>;
|
||||
using vviii = vector<viii>;
|
||||
using vb = vector<bool>;
|
||||
using vvb = vector<vb>;
|
||||
|
||||
/** WARNING **
|
||||
*************
|
||||
|
||||
This algorithm considers the graph to have no self-pointing edge, ie. no edge
|
||||
(u, u)
|
||||
*/
|
||||
|
||||
const int INFTY = 1000*1000*1000; // FIXME large enough for this problem?
|
||||
|
||||
struct Edge {
|
||||
int from, to;
|
||||
int weight;
|
||||
Edge() {}
|
||||
Edge(int from, int to, int weight): from(from), to(to), weight(weight) {}
|
||||
};
|
||||
|
||||
struct Vert {
|
||||
int selfId;
|
||||
int ufFather;
|
||||
int ufHeight;
|
||||
vector<int> out;
|
||||
vector<int> in;
|
||||
int parentEdge;
|
||||
bool suppressed;
|
||||
|
||||
Vert(int id):
|
||||
selfId(id), ufFather(id), ufHeight(1),
|
||||
parentEdge(-1), suppressed(false)
|
||||
{}
|
||||
};
|
||||
|
||||
int root;
|
||||
vector<Edge> edges;
|
||||
vector<Vert> vertices;
|
||||
int cost = 0;
|
||||
|
||||
int ufFind(int vert) {
|
||||
Vert& v = vertices[vert];
|
||||
if(v.ufFather == vert)
|
||||
return vert;
|
||||
v.ufFather = ufFind(v.ufFather);
|
||||
return v.ufFather;
|
||||
}
|
||||
|
||||
void ufUnion(int iv1, int iv2) {
|
||||
iv1 = ufFind(iv1);
|
||||
iv2 = ufFind(iv2);
|
||||
Vert& v1 = vertices[iv1];
|
||||
Vert& v2 = vertices[iv2];
|
||||
if(v1.ufHeight < v2.ufHeight)
|
||||
v1.ufFather = iv2;
|
||||
else if(v2.ufHeight < v1.ufHeight)
|
||||
v2.ufFather = iv1;
|
||||
else {
|
||||
v1.ufFather = iv2;
|
||||
v2.ufHeight++;
|
||||
}
|
||||
}
|
||||
|
||||
void buildMst(int vertId) {
|
||||
printf("> %d\n", vertId);
|
||||
Vert& vert = vertices[vertId];
|
||||
int minIncWeight = INFTY;
|
||||
int minId = -1;
|
||||
|
||||
for(int incEdge: vert.in) {
|
||||
Edge& edge = edges[incEdge];
|
||||
if(minIncWeight > edge.weight) {
|
||||
minIncWeight = edge.weight;
|
||||
minId = incEdge;
|
||||
}
|
||||
}
|
||||
|
||||
if(minId == -1) {
|
||||
assert(vertId == root);
|
||||
return;
|
||||
}
|
||||
|
||||
for(int incEdge: vert.in) {
|
||||
Edge& edge = edges[incEdge];
|
||||
edge.weight -= minIncWeight;
|
||||
}
|
||||
cost += minIncWeight;
|
||||
|
||||
vert.parentEdge = minId;
|
||||
int parent = edges[minId].from;
|
||||
int parentCmp = ufFind(parent), selfCmp = ufFind(vertId);
|
||||
if(parentCmp == selfCmp) {
|
||||
// Build current cycle
|
||||
vector<int> cycle;
|
||||
cycle.push_back(parent);
|
||||
while(cycle.back() != vertId)
|
||||
cycle.push_back(edges[vertices[cycle.back()].parentEdge].from);
|
||||
|
||||
vertices.push_back(Vert(vertices.size()));
|
||||
Vert& condensed = vertices.back();
|
||||
printf("Compressing into %d: ", condensed.selfId);
|
||||
for(int cyc: cycle)
|
||||
printf("%d ", cyc);
|
||||
puts("");
|
||||
vector<int> nInEdges, nOutEdges;
|
||||
for(int cycleVert: cycle) {
|
||||
Vert& v = vertices[cycleVert];
|
||||
v.suppressed = true;
|
||||
for(int inEdge: v.in) {
|
||||
edges[inEdge].to = condensed.selfId;
|
||||
nInEdges.push_back(inEdge);
|
||||
}
|
||||
for(int outEdge: v.out) {
|
||||
edges[outEdge].from = condensed.selfId;
|
||||
if(edges[outEdge].to != condensed.selfId)
|
||||
condensed.out.push_back(outEdge);
|
||||
}
|
||||
}
|
||||
|
||||
for(int inEdge: nInEdges) {
|
||||
if(edges[inEdge].from != condensed.selfId)
|
||||
condensed.in.push_back(inEdge);
|
||||
}
|
||||
|
||||
ufUnion(vertId, condensed.selfId);
|
||||
buildMst(condensed.selfId);
|
||||
}
|
||||
else {
|
||||
ufUnion(vertId, parent);
|
||||
if(ufFind(vertId) != ufFind(root))
|
||||
buildMst(parent);
|
||||
}
|
||||
}
|
||||
|
||||
void dfs(int pos, vector<bool>& seen) {
|
||||
if(seen[pos])
|
||||
return;
|
||||
seen[pos] = true;
|
||||
for(int outEdge: vertices[pos].out)
|
||||
dfs(edges[outEdge].to, seen);
|
||||
}
|
||||
|
||||
bool allReachable() {
|
||||
vector<bool> seen(vertices.size(), false);
|
||||
dfs(root, seen);
|
||||
for(size_t pos=0; pos < seen.size(); ++pos) {
|
||||
if(!seen[pos]) {
|
||||
fprintf(stderr, "NOT REACHABLE: %lu\n", pos);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int main() {
|
||||
int nbVert, nbEdge;
|
||||
scanf("%d%d%d", &nbVert, &nbEdge, &root);
|
||||
for(int vert=0; vert < nbVert; ++vert)
|
||||
vertices.push_back(Vert(vert));
|
||||
for(int edgeId=0; edgeId < nbEdge; ++edgeId) {
|
||||
Edge edge;
|
||||
scanf("%d%d%d", &edge.from, &edge.to, &edge.weight);
|
||||
edges.push_back(edge);
|
||||
vertices[edge.from].out.push_back(edges.size() - 1);
|
||||
vertices[edge.to].in.push_back(edges.size() - 1);
|
||||
}
|
||||
|
||||
if(!allReachable()) {
|
||||
printf("-1\n");
|
||||
puts("BLEH");
|
||||
return 0;
|
||||
}
|
||||
|
||||
for(int vert=0; vert < nbVert; ++vert) {
|
||||
if(vertices[vert].suppressed || ufFind(vert) == root)
|
||||
continue;
|
||||
buildMst(vert);
|
||||
}
|
||||
|
||||
printf("%d\n", cost);
|
||||
}
|
5
day2/j/01.in
Normal file
5
day2/j/01.in
Normal file
|
@ -0,0 +1,5 @@
|
|||
3 2 11
|
||||
1
|
||||
3
|
||||
1
|
||||
|
175
day2/j/j.cpp
Normal file
175
day2/j/j.cpp
Normal file
|
@ -0,0 +1,175 @@
|
|||
#include <bits/stdc++.h>
|
||||
|
||||
#define FOR(i, n) for(lli i = 0; i < (lli)(n); ++i)
|
||||
#define FORU(i, a, b) for(lli i = (lli)(a); i < (lli)(b); ++i)
|
||||
#define FORD(i, a, b) for(lli i = (lli)(b)-1; i >= (lli)(a); --i)
|
||||
#define ALL(x) (x).begin(), (x).end()
|
||||
|
||||
#define X(A) get<0>(A)
|
||||
#define Y(A) get<1>(A)
|
||||
#define Z(A) get<2>(A)
|
||||
#define W(A) get<3>(A)
|
||||
|
||||
#define mt make_tuple
|
||||
#define pb push_back
|
||||
|
||||
|
||||
|
||||
using namespace std;
|
||||
using lli = long long int;
|
||||
|
||||
using pii = tuple<lli, lli>;
|
||||
using piii = tuple<lli, lli, lli>;
|
||||
using vi = vector<lli>;
|
||||
using vii = vector<pii>;
|
||||
using viii = vector<piii>;
|
||||
using vvi = vector<vi>;
|
||||
using vvii = vector<vii>;
|
||||
using vviii = vector<viii>;
|
||||
using vb = vector<bool>;
|
||||
using vvb = vector<vb>;
|
||||
|
||||
const lli INFTY = ((1ll << 60) - 1) * 2;
|
||||
|
||||
// =====
|
||||
|
||||
struct Evt {
|
||||
enum {
|
||||
JACKPOT, ENTRY
|
||||
} type;
|
||||
|
||||
lli date;
|
||||
lli nbPers;
|
||||
|
||||
bool operator<(const Evt& e) const {
|
||||
if(date == e.date)
|
||||
return type == Evt::ENTRY;
|
||||
return date < e.date;
|
||||
}
|
||||
};
|
||||
|
||||
lli gcd(lli a, lli b) {
|
||||
if(b == 0)
|
||||
return a;
|
||||
return gcd(b, a % b);
|
||||
}
|
||||
|
||||
struct Frac {
|
||||
Frac() : num(0), den(1) {}
|
||||
Frac(lli n, lli d): num(n), den(d) { simpl(); }
|
||||
lli num, den;
|
||||
|
||||
void simpl() {
|
||||
lli g = gcd(num, den);
|
||||
num /= g;
|
||||
den /= g;
|
||||
}
|
||||
|
||||
/*
|
||||
Frac operator+(const Frac& b) {
|
||||
return Frac(b.den * num + den * b.num, den * b.den);
|
||||
}
|
||||
*/
|
||||
|
||||
double val() const {
|
||||
return ((double)num / (double)den);
|
||||
}
|
||||
|
||||
Frac& operator*=(const Frac& b) {
|
||||
num *= b.num;
|
||||
den *= b.den;
|
||||
simpl();
|
||||
return *this;
|
||||
}
|
||||
|
||||
double operator*(double b) const {
|
||||
return b * val();
|
||||
}
|
||||
};
|
||||
|
||||
vector<lli> joinTimes;
|
||||
vector<Evt> jackpots;
|
||||
lli ticketPrice, jackpotVal;
|
||||
map<lli, double> expec;
|
||||
|
||||
void getJackpots() {
|
||||
lli slope = 0, cMoney = 0;
|
||||
vector<lli> orderedJoins(joinTimes);
|
||||
sort(orderedJoins.begin(), orderedJoins.end());
|
||||
orderedJoins.push_back(INFTY);
|
||||
|
||||
lli pEvent = 0;
|
||||
|
||||
for(size_t j = 0; j < orderedJoins.size() - 1; ++j) {
|
||||
lli join = orderedJoins[j];
|
||||
lli nEvent = orderedJoins[j+1];
|
||||
cMoney += (join - pEvent) * slope;
|
||||
slope += ticketPrice;
|
||||
pEvent = join;
|
||||
while(cMoney + (nEvent - pEvent - 1) * slope >= jackpotVal) {
|
||||
jackpots.push_back(Evt({Evt::JACKPOT,
|
||||
(jackpotVal - cMoney + slope - 1) / slope + pEvent,
|
||||
0}));
|
||||
pEvent = jackpots.back().date;
|
||||
slope -= ticketPrice;
|
||||
cMoney = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void fillExpec() {
|
||||
vector<Evt> events(jackpots);
|
||||
for(lli join: joinTimes)
|
||||
events.push_back(Evt({Evt::ENTRY, join, 0}));
|
||||
sort(events.begin(), events.end());
|
||||
int cPers = 0;
|
||||
for(Evt& evt: events) {
|
||||
cPers += (evt.type == Evt::ENTRY) ? 1 : -1;
|
||||
evt.nbPers = cPers + ((evt.type == Evt::JACKPOT)?1:0);
|
||||
}
|
||||
|
||||
double cExpec = 0;
|
||||
Frac cProd;
|
||||
for(auto evtIt = events.begin(); evtIt != events.end(); ++evtIt) {
|
||||
const Evt& evt = *evtIt;
|
||||
if(evt.nbPers == 1 && evt.type == Evt::ENTRY) {
|
||||
cProd = Frac(1, 1);
|
||||
cExpec = 0;
|
||||
for(auto cJack = evtIt; cJack != events.end(); ++cJack) {
|
||||
if(cJack->type != Evt::JACKPOT)
|
||||
continue;
|
||||
|
||||
cExpec +=
|
||||
cProd * ((double)cJack->date) / ((double)cJack->nbPers);
|
||||
cProd *= Frac(cJack->nbPers - 1, cJack->nbPers);
|
||||
|
||||
if(cJack->nbPers == 1)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(evt.type == Evt::JACKPOT) {
|
||||
cExpec -= ((double)evt.date) / ((double)evt.nbPers);
|
||||
cExpec *= ((double)evt.nbPers) / ((double)evt.nbPers - 1.);
|
||||
}
|
||||
else {
|
||||
expec.insert({evt.date, ticketPrice * (cExpec - evt.date)});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
int nbPlayers;
|
||||
scanf("%d%lld%lld", &nbPlayers, &ticketPrice, &jackpotVal);
|
||||
ticketPrice /= 2;
|
||||
joinTimes.resize(nbPlayers);
|
||||
FOR(i, nbPlayers)
|
||||
scanf("%lld", &(joinTimes[i]));
|
||||
getJackpots();
|
||||
fillExpec();
|
||||
|
||||
for(const lli& joinTime: joinTimes) {
|
||||
printf("%.15lf\n", expec[joinTime]);
|
||||
}
|
||||
}
|
||||
|
3
day3/g/01.in
Normal file
3
day3/g/01.in
Normal file
|
@ -0,0 +1,3 @@
|
|||
2 3
|
||||
WWB
|
||||
BBB
|
4
day3/g/03.in
Normal file
4
day3/g/03.in
Normal file
|
@ -0,0 +1,4 @@
|
|||
3 3
|
||||
BBB
|
||||
WBW
|
||||
WBW
|
4
day3/g/2.in
Normal file
4
day3/g/2.in
Normal file
|
@ -0,0 +1,4 @@
|
|||
3 3
|
||||
WWW
|
||||
WBW
|
||||
WWW
|
135
day3/g/g.cpp
Normal file
135
day3/g/g.cpp
Normal file
|
@ -0,0 +1,135 @@
|
|||
#include <bits/stdc++.h>
|
||||
|
||||
#define FOR(i, n) for(lli i = 0; i < (lli)(n); ++i)
|
||||
#define FORU(i, a, b) for(lli i = (lli)(a); i < (lli)(b); ++i)
|
||||
#define FORD(i, a, b) for(lli i = (lli)(b)-1; i >= (lli)(a); --i)
|
||||
#define ALL(x) (x).begin(), (x).end()
|
||||
|
||||
#define X(A) get<0>(A)
|
||||
#define Y(A) get<1>(A)
|
||||
#define Z(A) get<2>(A)
|
||||
#define W(A) get<3>(A)
|
||||
|
||||
#define mt make_tuple
|
||||
#define pb push_back
|
||||
|
||||
|
||||
|
||||
using namespace std;
|
||||
using lli = long long int;
|
||||
|
||||
using pii = tuple<lli, lli>;
|
||||
using piii = tuple<lli, lli, lli>;
|
||||
using vi = vector<lli>;
|
||||
using vii = vector<pii>;
|
||||
using viii = vector<piii>;
|
||||
using vvi = vector<vi>;
|
||||
using vvii = vector<vii>;
|
||||
using vviii = vector<viii>;
|
||||
using vb = vector<bool>;
|
||||
using vvb = vector<vb>;
|
||||
// =====
|
||||
|
||||
const int DIM = 50 * 50 + 1;
|
||||
int nbRows, nbCols, nbDim;
|
||||
|
||||
typedef bitset<DIM> Matr;
|
||||
|
||||
struct PermVect {
|
||||
vector<Matr> v;
|
||||
vector<int> lineId;
|
||||
|
||||
PermVect(){}
|
||||
PermVect(size_t n) {
|
||||
v.resize(n);
|
||||
lineId.resize(n);
|
||||
FOR(i, lineId.size()) lineId[i] = i;
|
||||
}
|
||||
|
||||
Matr& operator[](size_t pos) { return v[lineId[pos]]; }
|
||||
const Matr& operator[](size_t pos) const { return v[lineId[pos]]; }
|
||||
void swapRow(size_t i, size_t j) {
|
||||
swap(lineId[i], lineId[j]);
|
||||
}
|
||||
};
|
||||
|
||||
PermVect crosses;
|
||||
|
||||
inline size_t at(int row, int col) {
|
||||
return row * nbCols + col;
|
||||
}
|
||||
|
||||
void dump() {
|
||||
FOR(rowId, nbDim) {
|
||||
const Matr& row = crosses[rowId];
|
||||
FOR(pos, nbDim)
|
||||
printf("%c", row[pos]?'1':'0');
|
||||
printf(" %c\n", row[nbDim]?'1':'0');
|
||||
}
|
||||
puts("===");
|
||||
}
|
||||
|
||||
bool solve() {
|
||||
for(int trigAt=0; trigAt < nbDim; ++trigAt) {
|
||||
bool found = false;
|
||||
for(int dim=trigAt; dim < nbDim; ++dim) {
|
||||
if(!crosses[dim][trigAt])
|
||||
continue;
|
||||
found = true;
|
||||
crosses.swapRow(trigAt, dim);
|
||||
for(int nDim=trigAt+1; nDim < nbDim; ++nDim) {
|
||||
if(crosses[nDim][trigAt])
|
||||
crosses[nDim] ^= crosses[trigAt];
|
||||
}
|
||||
break;
|
||||
}
|
||||
if(!found)
|
||||
return false;
|
||||
}
|
||||
for(int dim=nbDim-1; dim >= 0; --dim) {
|
||||
for(int nDim=dim-1; nDim >= 0; --nDim) {
|
||||
if(crosses[nDim][dim])
|
||||
crosses[nDim] ^= crosses[dim];
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
scanf("%d%d", &nbRows, &nbCols);
|
||||
nbDim = nbRows * nbCols - 1;
|
||||
crosses = PermVect(nbDim);
|
||||
FOR(row, nbRows) {
|
||||
FOR(col, nbCols) {
|
||||
FOR(i, nbCols)
|
||||
crosses[at(row, col)].set(at(row, i));
|
||||
FOR(i, nbRows)
|
||||
crosses[at(row, col)].set(at(i, col));
|
||||
}
|
||||
}
|
||||
|
||||
FOR(row, nbRows) {
|
||||
getchar();
|
||||
FOR(col, nbCols)
|
||||
crosses[at(row, col)][nbDim] = getchar() == 'B';
|
||||
}
|
||||
|
||||
|
||||
if(!solve()) {
|
||||
dump();
|
||||
puts("No");
|
||||
}
|
||||
else {
|
||||
puts("Yes");
|
||||
set<int> ids;
|
||||
FOR(row, nbDim)
|
||||
if(crosses[row][nbDim])
|
||||
ids.insert(row);
|
||||
printf("%lu\n", ids.size());
|
||||
FOR(i, nbDim) {
|
||||
if(ids.find(crosses.lineId[i]) != ids.end())
|
||||
printf("%lld %lld\n", (i/nbCols) + 1, (i%nbCols) + 1);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
212
day3/g/g.orig.cpp
Normal file
212
day3/g/g.orig.cpp
Normal file
|
@ -0,0 +1,212 @@
|
|||
#include <bits/stdc++.h>
|
||||
|
||||
#define FOR(i, n) for(lli i = 0; i < (lli)(n); ++i)
|
||||
|
||||
#define X(A) get<0>(A)
|
||||
#define Y(A) get<1>(A)
|
||||
#define Z(A) get<2>(A)
|
||||
#define W(A) get<3>(A)
|
||||
|
||||
#define mt make_tuple
|
||||
#define mp make_pair
|
||||
#define pb push_back
|
||||
|
||||
#define fst first
|
||||
#define snd second
|
||||
|
||||
|
||||
using namespace std;
|
||||
using lli = long long int;
|
||||
using ll = long long int;
|
||||
|
||||
using pii = tuple<lli, lli>;
|
||||
using piii = tuple<lli, lli, lli>;
|
||||
using vi = vector<lli>;
|
||||
using vii = vector<pii>;
|
||||
using viii = vector<piii>;
|
||||
using vvi = vector<vi>;
|
||||
using vvii = vector<vii>;
|
||||
using vviii = vector<viii>;
|
||||
using vb = vector<bool>;
|
||||
using vvb = vector<vb>;
|
||||
|
||||
|
||||
template<int N> // N = dimension
|
||||
struct gauss {
|
||||
bitset<N> A[N]; // upper diagonal matrix (free family)
|
||||
int V[N]; int nv=0; // identify the columns
|
||||
bitset<N> B[N]; // how elements of A are generated ?
|
||||
|
||||
gauss() { reset(); }
|
||||
void reset() {
|
||||
// memset(this,0,sizeof(gauss<N>)); should work as well
|
||||
FOR(i,N) A[i].reset();
|
||||
nv=0;
|
||||
FOR(i,N) B[i].reset();
|
||||
}
|
||||
|
||||
// add a matrix column (a vector in the free family)
|
||||
void add(int id, bitset<N> x) {
|
||||
if(nv == N) return;
|
||||
bitset<N> y; y[nv]=1;
|
||||
FOR(i,N) if(x[i]) {
|
||||
if(A[i][i]) { x ^= A[i]; y ^= B[i]; }
|
||||
else { A[i] = x; V[nv++] = id; B[i] = y; return; }
|
||||
}
|
||||
}
|
||||
|
||||
// reach a vector x
|
||||
// returns true when possible, false otherwise
|
||||
// y contains the vectors of V used
|
||||
bool solve(bitset<N> x, bitset<N> &y){
|
||||
y.reset();
|
||||
FOR(i,N) if(x[i]&&A[i][i]) {
|
||||
x ^= A[i];
|
||||
y ^= B[i];
|
||||
}
|
||||
if(x.any()) return false;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// example usage : solve a linear system in Z/2Z
|
||||
// M is a matrix (list of columns)
|
||||
// X is a vector
|
||||
// solves M Y = X
|
||||
// returns in Y the indices of columns of M with xor X
|
||||
const int DIM=2500;
|
||||
bool solve(vector<bitset<DIM> > M, bitset<DIM> X, vi &Y) {
|
||||
gauss<DIM> G;
|
||||
FOR(i,M.size()) {
|
||||
G.add(i, M[i]);
|
||||
}
|
||||
bitset<DIM> y;
|
||||
if (!G.solve(X, y))
|
||||
return false;
|
||||
for (int i = 0; i < DIM; ++i)
|
||||
if (y[i])
|
||||
Y.pb(G.V[i]);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Inspire de https://github.com/stjepang/snippets/blob/master/gauss.cpp
|
||||
// Elimination de Gauss
|
||||
// Resout un systeme d'equations lineaires
|
||||
// Complexite : O(nb_lins * nb_cols^2)
|
||||
// Si le systeme a au moins une solution, value contiendra une solution possible
|
||||
const int MAX_NB_COLS = 2500;
|
||||
const double eps = 1e-8;
|
||||
|
||||
struct Gauss {
|
||||
// posnz[i] = -1 si la i-eme composante est libre
|
||||
int posnz[MAX_NB_COLS];
|
||||
// value[i] = la valeur de X(i) verifiant l'equation ci-dessous
|
||||
int value[MAX_NB_COLS];
|
||||
// vrai ssi. le systeme a >= 1 solution
|
||||
bool has_solution;
|
||||
|
||||
// mat[0 .. nb_lins-1][0 .. nb_cols-1] * X = mat[0 .. nb_lins-1][nb_cols]
|
||||
Gauss(int mat[][MAX_NB_COLS + 1], int nb_lins, int nb_cols) {
|
||||
fill(posnz, posnz + nb_cols, -1);
|
||||
|
||||
|
||||
for (int i = 0; i < nb_lins; ++i, puts(""))
|
||||
for (int j = 0; j < nb_cols; ++j)
|
||||
printf("%d ", mat[i][j]);
|
||||
int posnz_cur = 0;
|
||||
for (int col = 0; col < nb_cols; ++col) {
|
||||
int max_lin = posnz_cur;
|
||||
|
||||
for (int lin = max_lin + 1; lin < nb_lins; ++lin)
|
||||
if (fabs(mat[lin][col]) > fabs(mat[max_lin][col]))
|
||||
max_lin = lin;
|
||||
|
||||
// La colonne est nulle
|
||||
// Condition de la forme == 0 si on est dans les entiers
|
||||
if (mat[max_lin][col] == 0) continue;
|
||||
|
||||
for (int i = 0; i <= nb_cols; ++i)
|
||||
swap(mat[max_lin][i], mat[posnz_cur][i]);
|
||||
|
||||
for (int lin = 0; lin < nb_lins; ++lin) {
|
||||
if (lin == posnz_cur) continue;
|
||||
// Pour Gauss modulaire : remplacer par l'inverse de mat[posnz_cur][col]
|
||||
int factor = mat[lin][col] * (1 ^ mat[posnz_cur][col]);
|
||||
for (int i = 0; i <= nb_cols; ++i) {
|
||||
mat[lin][i] -= factor * mat[posnz_cur][i];
|
||||
mat[lin][i] %= 2;
|
||||
mat[lin][i] += 2;
|
||||
mat[lin][i] %= 2;
|
||||
}
|
||||
// Gauss mod : rajouter le modulo
|
||||
}
|
||||
|
||||
posnz[col] = posnz_cur++;
|
||||
}
|
||||
|
||||
// Genere une solution valide
|
||||
for (int col = 0; col < nb_cols; ++col) {
|
||||
if (posnz[col] != -1)
|
||||
value[col] = mat[posnz[col]][nb_cols] * (1 ^ mat[posnz[col]][col]);
|
||||
// Gauss mod
|
||||
else
|
||||
value[col] = 0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < nb_lins; ++i, puts(""))
|
||||
for (int j = 0; j < nb_cols; ++j)
|
||||
printf("%d ", mat[i][j]);
|
||||
|
||||
// Verifie que la solution generee est valide
|
||||
has_solution = true;
|
||||
for (int lin = 0; lin < nb_lins; ++lin) {
|
||||
int sum = 0;
|
||||
for (int col = 0; col < nb_cols; ++col) {
|
||||
sum += mat[lin][col] * value[col]; // Gauss mod
|
||||
sum %= 2;
|
||||
}
|
||||
if (sum - mat[lin][nb_cols] != 0) // Gauss mod
|
||||
has_solution = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const int MAXN = 50, MAXM = 50;
|
||||
int grid[MAXN][MAXM];
|
||||
int mat[MAXN * MAXM][MAXN * MAXM + 1];
|
||||
int N, M;
|
||||
|
||||
int linof(int i) { return i / M; }
|
||||
int colof(int i) { return i % M; }
|
||||
|
||||
int main() {
|
||||
scanf("%d%d", &N, &M);
|
||||
for (int i = 0; i < N; ++i)
|
||||
for (int j = 0; j < M; ++j) {
|
||||
char c;
|
||||
scanf(" %c", &c);
|
||||
grid[i][j] = c == 'W';
|
||||
}
|
||||
vector<bitset<DIM>> mat(DIM);
|
||||
bitset<DIM> X;
|
||||
for (int i = 0; i < N * M; ++i)
|
||||
for (int j = 0; j < N * M; ++j)
|
||||
mat[i][j] = linof(i) == linof(j) || colof(i) == colof(j);
|
||||
for (int i = 0; i < N * M; ++i)
|
||||
X[i] = grid[linof(i)][colof(i)] ^ 1;
|
||||
/*
|
||||
for (int i = 0; i < N * M; ++i)
|
||||
mat[i][N * M] = grid[linof(i)][colof(i)] ^ 1;
|
||||
*/
|
||||
vi Y;
|
||||
if (solve(mat, X, Y)) {
|
||||
puts("Yes");
|
||||
printf("%d\n", (int)Y.size());
|
||||
for (int x: Y)
|
||||
printf("%d %d\n", linof(x) + 1, colof(x) + 1);
|
||||
} else
|
||||
puts("No");
|
||||
return 0;
|
||||
}
|
7
day3/g/gen.py
Normal file
7
day3/g/gen.py
Normal file
|
@ -0,0 +1,7 @@
|
|||
from random import randint
|
||||
|
||||
print("50 50")
|
||||
for row in range(50):
|
||||
for col in range(50):
|
||||
print('B' if randint(0, 1) else 'W', end='')
|
||||
print('')
|
51
day3/g/large.in
Normal file
51
day3/g/large.in
Normal file
|
@ -0,0 +1,51 @@
|
|||
50 50
|
||||
BWBBWWBWWBBBBWWWWBWWBBWBBWWBWBBWBWBWWBWWBBWBBWBBBW
|
||||
WBWWWWWBBWBWWBBBBBWWBWWWBWBWBBBBWWBBWWWBWBBBBBBBWW
|
||||
WWWBWBBBWWBBBBWBBBWBBWBWWWWBBBWBWWWBWWBBWWBWWWWBWB
|
||||
BBBWWWWWWBBWWBWWBBWWBWBBWWWBWWWBWWBBBBBWBWBBBBBBBB
|
||||
BBBWBWWWBBBBWBWWBBBBWWBWBBBBWBWBWBBWWWWBBBBWBWBBBB
|
||||
WWBBWWWWBBWBBBBWBBBWBBWWWBWBWBBWWWWBBWBWWWBBBWBBWW
|
||||
BBBWBBBWWWWBWWBWBWBBBBWBBWWBWBBBWBBBWWWWBBBBWBBWWW
|
||||
WBBWBWWBWWBWBWBWWBBWBWWWWBWBBBBBBBWBWWWWWWBBBWBWBB
|
||||
BWBBWWBWBWWBBBBBBWWWBWWWBWWBBBBBBWBWWWWBWWBWWBBWWW
|
||||
WWWBWWWWWWWWBWWBWBWWBWBWWWWWWBWBBWBWWBWWBWBBBBWWBB
|
||||
BBWWBWBWBWWWWBBWWWBBBBBBBBWWBBBBWWBWWBBWWWWWWWBWWW
|
||||
BWBWWWWBBBWWWBWWWWBWWBBBBWWBWWBBBWWWBWBBBBBWWBBBWB
|
||||
BWWWWWWWBWWBBBBBBWWWWBWBWWWWWBWBWWBBBWBBWBWWBBWWWB
|
||||
WBBBWWBWWBWWBWBWBBBBBBBWWWBWBWBWWWWBWBBWBBBWWBWBWW
|
||||
WBBWWBWWWBBBBWBBWBBBBBWBWWBBBBBBWWWBWBBWBBBBWBBBWW
|
||||
WWBWWBBWWWWWWWBWBBWBBWBWWWWBWWBBWWWBWBBWWBWWWWWBWW
|
||||
BBBWBWWWWBWBBWWWBWWBWWBWWBWBWBBBBBWBWBWBWBBWBWBBWW
|
||||
WBWBWWWBBWBWBBBBWBBBWBWBWBBBBWBBWBWBWBBBWWWWBBBBWB
|
||||
WWWBWBWBWBWWWBBBWBBBBBWWBBBWWWWWWWWBWWBWWBBWBBWBWB
|
||||
BBWWWWWBBBWBBWWBBWWBBBWBWBBWBBWWWBBWWBWWWBWBWWWWBB
|
||||
WWBWBBBBBWBBBWBWWWWWWBWWBWBWWWWWWBBBWWWBWBBBWBBBWB
|
||||
BBBWWBWBWBBWWBBWBWBWBWBWWBWBWWWWBBWWBWBBWWBBWWWBWW
|
||||
BBWWBBWWWBBWBWBWBWWBWWBWBBWWBBWWWBBWWBWWWWBBWWBWWB
|
||||
WWBBBBBBWWBBBWBBWBBWWBBWWBWBWWBWBBBBWWWWWWBBBBBBBW
|
||||
WWBWWWWWBWBBWBWWBBBBWWWBWWWBWBBWBBBWWBBWWBBBWWBWBB
|
||||
WBBWBBBBBWBBWBBWWBBBBBBWBWBBBWWBWBWBWBWBWWWBBBBWBB
|
||||
BWWBBWBWBBWWWWWBBBWWBWWBBBBBBWWWBWWWBBWBBWWWWWBBWW
|
||||
WWBBBWBWBWWWWBBWWWBWWBWWWBWWBBBWBBWWBWWBBWBBWWWBBW
|
||||
WBWWBWBWBWWBBBBWBWWWBWWWBWBBWWWBWBBBWBBBBWBWWBWWWB
|
||||
WBBWBWBBBBBWWWBWBWBWBBWBBBBBWWWBBWBBWWWBWWBBWWWBBB
|
||||
BBWBWWBWBBWBWWWBBWWWBWBWWWWWWBBBWWBWBBBWWBWBWWBWWB
|
||||
WWBBBBWBWWWBBWWBWWWBWBBWWWWBBBWBBWWBWBBBWBWWWBBWWW
|
||||
WWWBWWWBWBWWBWBBBWWWWBBWBWBBWBBBWWBBWBWWWBBBBWWWBB
|
||||
WBWWWBWWWBBBBWBWBBBWWBBWBWWWBWWWBBWBWWWBWWBBBBWWWB
|
||||
BBWBBWBWWBWWWWBWBWWWWWBBWBBBBWWWWBWBBBBWWWBBWWBBBB
|
||||
BBWBWWBBWBBWBBBWBWBWBBWWWWBBBWWWBWWBBBBBWWBBWBWBBB
|
||||
WBWBWWWBBWBBBBBWWBWWWBWBBWWBWBWBBWWBBBWBBWWBBBWWBB
|
||||
BBWWBWWBBBWBBBWBWBBWBWBBBBBWWBWBBWBWWBBBWWWWBBBBBW
|
||||
BBBWWWBBWWBWWBWWWBBWBWWBWWBWBBWBBBWWWBWBWWBWWWBWWB
|
||||
BWWWBBBWWWBBWWWWBBBWBWWBWWBBWWBWBWWWWWBBBWWBBWWBBW
|
||||
WWBBWBBBWBWBWWWBWWWWBBBWBBBBBBWWBBBBWWBBBWBWBBWBWB
|
||||
WBBBBWWWWBBWWWWWBWBBBWBWWBWBWWBBBWWWWWBWWBBWWWWWBW
|
||||
BBWWBWBWBBBBWWBWWWBWWBWWWBWBWBWWWWWBBWBWBBBWWBBBWW
|
||||
BWBWWBBWWBWBWBWWBWBWBWBBWBWBWWBBWWWBWBWWWBWBWBBBBB
|
||||
WWBWWBBBBBWBWBWBWBBWBWWWWWWBBWBWBWWWBWWBWBWWWWWBBB
|
||||
BBWBWBWBWWBBBBBBBWBWBBBBWBBWWBBWWWBBBBBBBWWWWWBBBW
|
||||
BWBWBWBWBWBWBBWBBWWBWBBWBWWWWBWWBWBWWBWBBWBBWWBBBW
|
||||
WWBWWBBWBWBWBBBBBBWBWBBWWBWBBBWBWBBBBBBWWBBWBWBWBW
|
||||
WWBBBBBWBWBWWWBWBBWWBWBWWBWBWWWBWWWBBWBBBWBWBWBBBB
|
||||
BWBBBBBWWBWWWBWWWBBBWWBWBWBBBBBBBWBWBBWWWBWWBBWWBB
|
136
day4/8.cpp
Normal file
136
day4/8.cpp
Normal file
|
@ -0,0 +1,136 @@
|
|||
#include <bits/stdc++.h>
|
||||
|
||||
#define FOR(i, n) for(lli i = 0; i < (lli)(n); ++i)
|
||||
#define ALL(x) (x).begin(), (x).end()
|
||||
|
||||
#define X(A) get<0>(A)
|
||||
#define Y(A) get<1>(A)
|
||||
#define Z(A) get<2>(A)
|
||||
#define W(A) get<3>(A)
|
||||
|
||||
#define mt make_tuple
|
||||
#define mp make_pair
|
||||
|
||||
using namespace std;
|
||||
using lli = long long int;
|
||||
|
||||
using pii = pair<lli, lli>;
|
||||
using piii = tuple<lli, lli, lli>;
|
||||
using vi = vector<lli>;
|
||||
using vii = vector<pii>;
|
||||
using viii = vector<piii>;
|
||||
using vvi = vector<vi>;
|
||||
using vvii = vector<vii>;
|
||||
using vviii = vector<viii>;
|
||||
using vb = vector<bool>;
|
||||
using vvb = vector<vb>;
|
||||
|
||||
const int BOUND = 1000*1000;
|
||||
//const int BOUND = 5;
|
||||
int nbChairs;
|
||||
|
||||
int ask(int x, int y) {
|
||||
printf("? %d %d\n", x, y);
|
||||
fflush(stdout);
|
||||
int out;
|
||||
scanf("%d", &out);
|
||||
return out;
|
||||
}
|
||||
|
||||
struct Query {
|
||||
unordered_map<int, int> memo;
|
||||
|
||||
int query(int v) {
|
||||
if(memo.find(v) != memo.end())
|
||||
return memo[v];
|
||||
memo[v] = int_query(v);
|
||||
return memo[v];
|
||||
}
|
||||
virtual int int_query(int v) = 0;
|
||||
};
|
||||
|
||||
struct RowQuery: public Query {
|
||||
int int_query(int v) {
|
||||
return ask(BOUND + 42, v);
|
||||
}
|
||||
};
|
||||
|
||||
struct ColQuery: public Query {
|
||||
int int_query(int v) {
|
||||
return ask(v, BOUND + 42);
|
||||
}
|
||||
};
|
||||
|
||||
struct FindQuery: public Query {
|
||||
vector<int> rows, cols;
|
||||
vii prev;
|
||||
vector<int> numInRow;
|
||||
int cRow, foundThisRow;
|
||||
FindQuery(): cRow(0), foundThisRow(0) {}
|
||||
|
||||
int int_query(int v) {
|
||||
assert(v < (int)cols.size());
|
||||
int ans = ask(cols[v], rows[cRow]);
|
||||
for(const pii& p: prev)
|
||||
if(p.second <= v && p.first < cRow)
|
||||
ans--;
|
||||
return ans;
|
||||
}
|
||||
void found(int col) {
|
||||
prev.push_back(mp(cRow, col));
|
||||
memo.clear();
|
||||
foundThisRow++;
|
||||
if(foundThisRow >= numInRow[cRow]) {
|
||||
cRow++;
|
||||
foundThisRow = 0;
|
||||
}
|
||||
}
|
||||
void addRow(int v) {
|
||||
if(rows.empty() || rows.back() != v) {
|
||||
rows.push_back(v);
|
||||
numInRow.push_back(1);
|
||||
}
|
||||
else
|
||||
numInRow.back()++;
|
||||
//printf("* Row %d (%d th)\n", v, numInRow.back());
|
||||
}
|
||||
void addCol(int v) {
|
||||
if(cols.empty() || cols.back() != v)
|
||||
cols.push_back(v);
|
||||
//printf("* Col %d\n", v);
|
||||
}
|
||||
};
|
||||
|
||||
int dicho(Query* qu, int beg, int end, int seek) {
|
||||
if(end - beg == 1)
|
||||
return beg;
|
||||
int m = (beg + end) / 2 - 1;
|
||||
int mVal = qu->query(m);
|
||||
if(mVal >= seek)
|
||||
return dicho(qu, beg, m+1, seek);
|
||||
return dicho(qu, m+1, end, seek);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
scanf("%d", &nbChairs);
|
||||
|
||||
FindQuery qFind;
|
||||
RowQuery qRow;
|
||||
ColQuery qCol;
|
||||
|
||||
FOR(i, nbChairs) {
|
||||
qFind.addRow(dicho(&qRow, -BOUND, BOUND+1, i+1));
|
||||
qFind.addCol(dicho(&qCol, -BOUND, BOUND+1, i+1));
|
||||
}
|
||||
|
||||
FOR(i, nbChairs) {
|
||||
int col = dicho(&qFind, 0, qFind.cols.size(), qFind.foundThisRow + 1);
|
||||
qFind.found(col);
|
||||
}
|
||||
printf("!");
|
||||
for(const pii& chair: qFind.prev)
|
||||
printf(" %d %d", qFind.cols[chair.second], qFind.rows[chair.first]);
|
||||
printf("\n");
|
||||
fflush(stdout);
|
||||
return 0;
|
||||
}
|
67
day4/8_int.py
Normal file
67
day4/8_int.py
Normal file
|
@ -0,0 +1,67 @@
|
|||
from random import randint
|
||||
from sys import stdin, stderr, stdout
|
||||
BOUND = 10**6
|
||||
#BOUND = 5
|
||||
|
||||
pts = []
|
||||
|
||||
while len(pts) < 100:
|
||||
x = randint(-BOUND, BOUND)
|
||||
y = randint(-BOUND, BOUND)
|
||||
if (x, y) in pts:
|
||||
continue
|
||||
pts.append((x, y))
|
||||
|
||||
print(len(pts))
|
||||
stdout.flush()
|
||||
|
||||
nbQuestions = 0
|
||||
|
||||
|
||||
def question(l):
|
||||
global nbQuestions
|
||||
x, y = [int(x) for x in l.strip().split()]
|
||||
nbQuestions += 1
|
||||
out = 0
|
||||
for (xi, yi) in pts:
|
||||
if xi <= x and yi <= y:
|
||||
out += 1
|
||||
return out
|
||||
|
||||
|
||||
def check(l):
|
||||
global nbQuestions
|
||||
gPts = set()
|
||||
print("{} questions, {} allowed".format(
|
||||
nbQuestions,
|
||||
50 * len(pts)), file=stderr)
|
||||
for pt in l:
|
||||
gPts.add(pt)
|
||||
if len(gPts) != len(pts):
|
||||
print('Fail', file=stderr)
|
||||
return
|
||||
for pt in gPts:
|
||||
if pt not in pts:
|
||||
print('Fail (not in)', file=stderr)
|
||||
return
|
||||
print('Success', file=stderr)
|
||||
|
||||
|
||||
while True:
|
||||
line = input()
|
||||
if line[0] == '!':
|
||||
spl = line[2:].strip().split()
|
||||
l = []
|
||||
for i in range(0, len(spl)//2):
|
||||
l.append((int(spl[2*i]), int(spl[2*i+1])))
|
||||
check(l)
|
||||
print(l, file=stderr)
|
||||
print(pts, file=stderr)
|
||||
break
|
||||
elif line[0] == '?':
|
||||
print(question(line[2:]))
|
||||
stdout.flush()
|
||||
elif line[0] == '*':
|
||||
print(line[1:], file=stderr)
|
||||
else:
|
||||
print("OMG", file=stderr)
|
3
day4/run.sh
Executable file
3
day4/run.sh
Executable file
|
@ -0,0 +1,3 @@
|
|||
mkfifo plug.fifo
|
||||
./a.out < plug.fifo | python 8_int.py > plug.fifo
|
||||
rm plug.fifo
|
380
misc/voronoi_twal.py
Normal file
380
misc/voronoi_twal.py
Normal file
|
@ -0,0 +1,380 @@
|
|||
import heapq
|
||||
import sys
|
||||
from math import sqrt
|
||||
from functools import reduce
|
||||
from random import randint
|
||||
|
||||
# J'ai utilisé dans cet exo l'algorithme de Fortune pour calculer le diagramme de Voronoï des points d'accès wifi
|
||||
# J'ai utilisé une skiplist à la place d'un arbre binaire balancé car j'ai un peu la flemme de coder un rb-tree ^^
|
||||
# Avant d'utiliser une skiplist, j'avais une liste doublement chaînée utilisant la même interface, car c'est plus facile à
|
||||
# coder et du coup moins de chance d'avoir des bugs. Avec j'avais une complexitée de O(n*(n+h)) où h est le nombre de points
|
||||
# sur l'enveloppe convexe. Avec une skiplist, j'ai une complexité de O(n*(log(n)+h)), mais j'ai été déçu car la constante
|
||||
# de temps fait que la skiplist devient avantageuse autour de N=20000, et l'énoncé donne N<=2000...
|
||||
# On m'a néanmoins conseillé de donner l'algorithme avec la meilleure complexité, donc la voici.
|
||||
|
||||
# Une priority queue car elles sont nécessairement synchronisées dans python... grr.
|
||||
class PriorityQueue:
|
||||
def __init__(self, elems):
|
||||
self.queue = [[prio, i, elem] for (i, (elem, prio)) in enumerate(elems)]
|
||||
self.entryFinder = {}
|
||||
for entry in self.queue:
|
||||
self.entryFinder[entry[2]] = entry
|
||||
heapq.heapify(self.queue)
|
||||
self.counter = len(self.queue)
|
||||
|
||||
def push(self, elem, priority):
|
||||
entry = [priority, self.counter, elem]
|
||||
self.entryFinder[elem] = entry
|
||||
heapq.heappush(self.queue, entry)
|
||||
self.counter += 1
|
||||
|
||||
def top(self):
|
||||
while self.queue:
|
||||
candidate = self.queue[0][2]
|
||||
if candidate != None:
|
||||
return candidate
|
||||
heapq.heappop(self.queue)
|
||||
return None
|
||||
|
||||
def pop(self):
|
||||
while self.queue:
|
||||
candidate = heapq.heappop(self.queue)[2]
|
||||
if candidate != None:
|
||||
del self.entryFinder[candidate]
|
||||
return candidate
|
||||
return None
|
||||
|
||||
def contains(self, elem):
|
||||
return elem in self.entryFinder
|
||||
|
||||
def remove(self, elem):
|
||||
entry = self.entryFinder.pop(elem)
|
||||
entry[2] = None
|
||||
|
||||
def empty(self):
|
||||
return self.top() == None
|
||||
|
||||
# Une classe représentant l'enveloppe convexe d'un ensemble de points
|
||||
class ConvexHull:
|
||||
def __init__(self, sites):
|
||||
# On la calcule avec un parcours de graham
|
||||
sortedSites = sorted(sites, key=lambda s:s.x)
|
||||
up = reduce(ConvexHull.keepLeft, sortedSites, [])
|
||||
down = reduce(ConvexHull.keepLeft, reversed(sortedSites), [])
|
||||
self.hull = up + down[1:-1]
|
||||
|
||||
def det(a, b, n):
|
||||
return (b.x-a.x)*(n.y-a.y) - (b.y-a.y)*(n.x-a.x)
|
||||
|
||||
def keepLeft(hull, n):
|
||||
while len(hull) > 1 and ConvexHull.det(hull[-2], hull[-1], n) <= 0:
|
||||
hull.pop()
|
||||
hull.append(n)
|
||||
return hull
|
||||
|
||||
def isLeftTo(a, b, s):
|
||||
return (s.x-a.x)*(b.y-a.y)-(s.y-a.y)*(b.x-a.x) < 0
|
||||
|
||||
def isInsideHull(self, s):
|
||||
for i in range(len(self.hull)):
|
||||
if not ConvexHull.isLeftTo(self.hull[i-1], self.hull[i], s):
|
||||
return False
|
||||
return True
|
||||
|
||||
# Classe représentant un point dans le plan. C'est nommé "Site" car c'est comme ça que sont nommés les points
|
||||
# dont il faut calculer le diagramme de voronoi dans les papiers de Fortune
|
||||
class Site:
|
||||
def __init__(self, x, y):
|
||||
self.x = x
|
||||
self.y = y
|
||||
|
||||
def dist(self, other):
|
||||
return sqrt((self.x-other.x)**2 + (self.y-other.y)**2)
|
||||
|
||||
# Une parabole de la beachline
|
||||
class Parabola:
|
||||
def __init__(self, ctx, site):
|
||||
self.ctx = ctx
|
||||
self.site = site
|
||||
self.ledge = None
|
||||
self.redge = None
|
||||
|
||||
def getEquation(self):
|
||||
x = self.site.x
|
||||
y = self.site.y
|
||||
l = self.ctx.sweepline
|
||||
div = 2*(y-l)
|
||||
if div == 0: #FIXME
|
||||
return None
|
||||
# Retourne (a, b, c) tels que y = a*x^2 + b*x + c
|
||||
# Obtenus en résolvant en y sqrt((x-xa)^2 + (y-ya)^2) = y-l avec xa = self.site.x, ya = self.site.y
|
||||
return (1/div, -2*x/div, (x*x + y*y - l*l)/div)
|
||||
|
||||
def intersect(self, other):
|
||||
if self.getEquation() == None:
|
||||
return self.site.x
|
||||
if other.getEquation() == None:
|
||||
return other.site.x
|
||||
(a, b, c) = tuple(map(lambda x, y: x-y, self.getEquation(), other.getEquation()))
|
||||
if a == 0: #FIXME
|
||||
# b*x + c = 0
|
||||
return -c/b
|
||||
else:
|
||||
# a*x^2 + b*x + c = 0
|
||||
discr = sqrt(b*b-4*a*c)
|
||||
x1 = (-b-discr)/(2*a)
|
||||
x2 = (-b+discr)/(2*a)
|
||||
if self.site.y < other.site.y:
|
||||
return min(x1, x2)
|
||||
else:
|
||||
return max(x1, x2)
|
||||
|
||||
# Donne le point où la parabole va être "écrasée" par ses deux voisines
|
||||
def circleCenter(self):
|
||||
return self.ledge.intersect(self.redge)
|
||||
|
||||
def hasCircleCenter(self):
|
||||
return self.ledge != None and self.redge != None and self.circleCenter() != None
|
||||
|
||||
# Renvoie la position de la sweepline quand la parabole va disparaitre
|
||||
def circleEventY(self):
|
||||
voronoiVertex = self.circleCenter()
|
||||
return voronoiVertex.y + voronoiVertex.dist(self.site)
|
||||
|
||||
# Renvoie une copie, sans ledge et redge car elles sont recalculées après une copie
|
||||
def copy(self):
|
||||
return Parabola(self.ctx, self.site)
|
||||
|
||||
# Représente une arête du diagramme de voronoï
|
||||
class Edge:
|
||||
def __init__(self, leftSite, rightSite):
|
||||
self.ls = leftSite
|
||||
self.rs = rightSite
|
||||
# a*x + b*y = c
|
||||
# Obtenus en résolvant en x et y sqrt((x-ls.x)^2 + (y-ls.y)^2) = sqrt((x-lr.x)^2 + (y-lr.y)^2)
|
||||
self.a = 2*(rightSite.x - leftSite.x)
|
||||
self.b = 2*(rightSite.y - leftSite.y)
|
||||
self.c = rightSite.x**2 + rightSite.y**2 - (leftSite.x**2 + leftSite.y**2)
|
||||
|
||||
def intersect(self, other):
|
||||
# Résolution via la méthode de Cramer
|
||||
det = self.a*other.b - self.b*other.a
|
||||
if det == 0: #FIXME
|
||||
return None
|
||||
return Site((self.c*other.b - self.b*other.c)/det, (self.a*other.c - self.c*other.a)/det)
|
||||
|
||||
# Représente un noeud d'une skip-list doublement chaînée
|
||||
class SkipListNode:
|
||||
def __init__(self, height, parabola):
|
||||
self.prv = [None]*height
|
||||
self.nxt = [None]*height
|
||||
self.parabola = parabola
|
||||
|
||||
# Représente une skiplist
|
||||
class SkipList:
|
||||
def __init__(self, elems):
|
||||
self.maxHeight = SkipList.randomHeight()
|
||||
self.head = SkipListNode(self.maxHeight, elems[0])
|
||||
updateList = [self.head]*self.maxHeight
|
||||
for i in range(1, len(elems)):
|
||||
self.insertAfter(elems[i], updateList)
|
||||
|
||||
# Renvoie une hauteur aléatoire
|
||||
def randomHeight():
|
||||
height = 1
|
||||
while randint(1, 2) != 1:
|
||||
height += 1
|
||||
return height
|
||||
|
||||
# Insertion d'un élément en fournissant le résultat de getElemByX() dans updateList
|
||||
# Ça met aussi à jour updateList pour insérer à nouveau un élément après celui qu'on vient d'insérer
|
||||
def insertAfter(self, elem, updateList):
|
||||
height = SkipList.randomHeight()
|
||||
self.maxHeight = max(height, self.maxHeight)
|
||||
newNode = SkipListNode(height, elem)
|
||||
# On ajuste la hauteur de updateList et self.head.nxt
|
||||
while len(self.head.nxt) < self.maxHeight:
|
||||
self.head.nxt.append(None)
|
||||
while len(updateList) < self.maxHeight:
|
||||
updateList.append(self.head)
|
||||
|
||||
for i in range(height):
|
||||
# Là c'est comme dans une liste doublement chaînée sauf qu'il y a des [i] partout
|
||||
newNode.prv[i] = updateList[i]
|
||||
newNode.nxt[i] = updateList[i].nxt[i]
|
||||
if updateList[i].nxt[i] != None:
|
||||
updateList[i].nxt[i].prv[i] = newNode
|
||||
updateList[i].nxt[i] = newNode
|
||||
updateList[i] = newNode
|
||||
return newNode
|
||||
|
||||
# Enlève un élément
|
||||
def remove(self, node):
|
||||
for i in range(len(node.nxt)-1, -1, -1):
|
||||
# Là c'est aussi comme pour une liste doublement chaînée mais avec des [i] partout
|
||||
node.prv[i].nxt[i] = node.nxt[i]
|
||||
if node.nxt[i] != None:
|
||||
node.nxt[i].prv[i] = node.prv[i]
|
||||
if self.head.nxt[i] == None:
|
||||
self.maxHeight -= 1
|
||||
|
||||
# On donne un X, et ça dit quelle est la parabole présente à cet endroit
|
||||
# Ça renvoie les éléments à modifier si on insère une autre parabole après celle cherchée
|
||||
def getElemByX(self, x):
|
||||
updateList = [None]*self.maxHeight
|
||||
node = self.head
|
||||
for i in range(self.maxHeight-1, -1, -1):
|
||||
while node.nxt[i] != None and self.getX(node) < x:
|
||||
node = node.nxt[i]
|
||||
# Souvent on "overshoot" la parabole et il faut revenir un peu en arrière
|
||||
if node.prv[0] != None and self.getX(node.prv[0]) > x:
|
||||
node = node.prv[i]
|
||||
updateList[i] = node
|
||||
return updateList
|
||||
|
||||
# Renvoie le x où node.parabola se termine
|
||||
def getX(self, node):
|
||||
return node.parabola.intersect(node.nxt[0].parabola)
|
||||
|
||||
# Représente un évènement, circle event ou site event
|
||||
class Event:
|
||||
def __init__(self, isSiteEvent, elem):
|
||||
self.isSiteEvent = isSiteEvent
|
||||
self.elem = elem
|
||||
|
||||
# Pour pouvoir enlever des éléments de la hashtable de la priority queue
|
||||
def __hash__(self):
|
||||
return hash(self.isSiteEvent) ^ hash(self.elem)
|
||||
|
||||
def __eq__(self, other):
|
||||
return other != None and self.isSiteEvent == other.isSiteEvent and self.elem == other.elem
|
||||
|
||||
class Context:
|
||||
def __init__(self, sites):
|
||||
self.sweepline = 0
|
||||
self.sites = [Site(x, y) for (x, y) in sites]
|
||||
minY = min([site.y for site in self.sites])
|
||||
# Les sites qui seront dans la beachline
|
||||
sitesBL = [site for site in self.sites if site.y == minY] #FIXME
|
||||
# Les sites qui seront dans la priority queue
|
||||
sitesQ = [site for site in self.sites if site.y != minY] #FIXME
|
||||
self.queue = PriorityQueue([(Event(True, site), site.y) for site in sitesQ])
|
||||
self.beachline = SkipList(list(map(lambda site: Parabola(self, site), sorted(sitesBL, key=lambda site: site.x))))
|
||||
# On calcule les arêtes associées aux paraboles de la beachline
|
||||
node = self.beachline.head
|
||||
while node.nxt[0] != None:
|
||||
self.updateREdge(node)
|
||||
node = node.nxt[0]
|
||||
self.sweepline = minY
|
||||
self.maxDist = 0
|
||||
self.hull = ConvexHull(self.sites)
|
||||
|
||||
# Enlève un circle event de la queue
|
||||
def removeCircleEvent(self, node):
|
||||
if self.queue.contains(Event(False, node)):
|
||||
self.queue.remove(Event(False, node))
|
||||
|
||||
# Ajoute un circle event dans la queue après quelques vérifications
|
||||
def addCircleEvent(self, node):
|
||||
def isRight(a, b, s):
|
||||
return (b.x-a.x)*(s.y-a.y)-(b.y-a.y)*(s.x-a.x) >= 0
|
||||
|
||||
parabola = node.parabola
|
||||
if parabola.hasCircleCenter():
|
||||
pos = parabola.circleEventY()
|
||||
center = parabola.circleCenter()
|
||||
# On ajoute l'event si il est derrière la sweepline et si la parabole va en effet disparaitre
|
||||
# (elle disparait si les points de la parabole de gauche, du milieu, de droite tourne dans le sens horaire, ce que calcule isRight)
|
||||
if pos > self.sweepline and isRight(node.prv[0].parabola.site, node.parabola.site, node.nxt[0].parabola.site):
|
||||
self.queue.push(Event(False, node), pos)
|
||||
|
||||
# Calcule l'arête à droite d'une parabole
|
||||
def updateREdge(self, node):
|
||||
if node.nxt[0] != None:
|
||||
edge = Edge(node.parabola.site, node.nxt[0].parabola.site)
|
||||
node.parabola.redge = edge
|
||||
node.nxt[0].parabola.ledge = edge
|
||||
|
||||
# On a trouvé un point du diagramme de voronoi ! C'est là qu'il risque d'y avoir des jaloux
|
||||
# Si le point du diagramme est en dehors de l'enveloppe convexe de tous les points alors
|
||||
# Il n'y aura pas de jaloux là, il faut donc ne pas mettre à jour self.maxDist
|
||||
def handleResult(self, v, p1, p2, p3):
|
||||
d = v.dist(p1)
|
||||
if d > self.maxDist and self.hull.isInsideHull(v):
|
||||
self.maxDist = d
|
||||
|
||||
# La partie intéressante de l'algorithme, le traitement d'un event
|
||||
def processEvent(self):
|
||||
event = self.queue.pop()
|
||||
# Si c'est un site event
|
||||
if event.isSiteEvent:
|
||||
# On récupère quelques infos
|
||||
site = event.elem
|
||||
self.sweepline = site.y
|
||||
updateList = self.beachline.getElemByX(site.x)
|
||||
leftNode = updateList[0]
|
||||
xpos = self.beachline.getX(leftNode) if leftNode.nxt[0] != None else float("nan")
|
||||
# On ajoute notre parabole
|
||||
middleNode = self.beachline.insertAfter(Parabola(self, site), updateList)
|
||||
# On enlève un éventuel circle event de la parabole qu'on "coupe"
|
||||
self.removeCircleEvent(leftNode)
|
||||
# Si on coupe une parabole
|
||||
if site.x != xpos: #FIXME
|
||||
# On ajoute la parabole qu'on coupe car pour l'instant il n'y a que la partie de gauche
|
||||
rightNode = self.beachline.insertAfter(leftNode.parabola.copy(), updateList)
|
||||
self.updateREdge(rightNode)
|
||||
# Si on s'intercale entre deux paraboles
|
||||
else:
|
||||
# On a trouvé un point du diagramme de voronoï, youhou !
|
||||
self.handleResult(Edge(site, leftNode.parabola.site).intersect(Edge(site, middleNode.nxt[0].parabola.site)), site, leftNode.parabola.site, middleNode.nxt[0].parabola.site)
|
||||
|
||||
# On re-calcule les arêtes
|
||||
self.updateREdge(leftNode)
|
||||
self.updateREdge(middleNode)
|
||||
|
||||
# On ajoute les éventuels circle events
|
||||
self.addCircleEvent(leftNode)
|
||||
if site.x != xpos: #FIXME
|
||||
self.addCircleEvent(rightNode)
|
||||
|
||||
# Si c'est un circle event
|
||||
else:
|
||||
# On récupère quelques infos
|
||||
middleNode = event.elem
|
||||
leftNode = middleNode.prv[0]
|
||||
rightNode = middleNode.nxt[0]
|
||||
self.sweepline = middleNode.parabola.circleEventY()
|
||||
|
||||
# On a trouvé un point du diagramme de voronoï, youhou !
|
||||
self.handleResult(middleNode.parabola.circleCenter(), leftNode.parabola.site, middleNode.parabola.site, rightNode.parabola.site)
|
||||
|
||||
# On enlève les éventuels circle events
|
||||
self.removeCircleEvent(leftNode)
|
||||
self.removeCircleEvent(rightNode)
|
||||
# On enlève la parabole qui disparait
|
||||
self.beachline.remove(middleNode)
|
||||
# On ajoute les nouveaux circle events
|
||||
self.updateREdge(leftNode)
|
||||
self.addCircleEvent(leftNode)
|
||||
self.addCircleEvent(rightNode)
|
||||
|
||||
def process(self):
|
||||
# Tant qu'il y a du boulot, on le traite !
|
||||
while not self.queue.empty():
|
||||
self.processEvent()
|
||||
|
||||
def wifi(N, coords):
|
||||
# La fonction "wifi" est maintenant très compliquée à coder
|
||||
ctx = Context(coords)
|
||||
ctx.process()
|
||||
return ctx.maxDist
|
||||
|
||||
if __name__ == '__main__':
|
||||
N = int(input())
|
||||
coords = [tuple(map(int, input().split())) for _ in range(N)]
|
||||
f = wifi(N, coords)
|
||||
if f == 0: #FIXME
|
||||
print("0")
|
||||
else:
|
||||
print("%.3f" % f)
|
||||
|
25
template.cpp
Normal file
25
template.cpp
Normal file
|
@ -0,0 +1,25 @@
|
|||
#include <bits/stdc++.h>
|
||||
|
||||
#define FOR(i, n) for(lli i = 0; i < (lli)(n); ++i)
|
||||
#define ALL(x) (x).begin(), (x).end()
|
||||
|
||||
#define X(A) get<0>(A)
|
||||
#define Y(A) get<1>(A)
|
||||
#define Z(A) get<2>(A)
|
||||
#define W(A) get<3>(A)
|
||||
|
||||
#define mt make_tuple
|
||||
|
||||
using namespace std;
|
||||
using lli = long long int;
|
||||
|
||||
using pii = tuple<lli, lli>;
|
||||
using piii = tuple<lli, lli, lli>;
|
||||
using vi = vector<lli>;
|
||||
using vii = vector<pii>;
|
||||
using viii = vector<piii>;
|
||||
using vvi = vector<vi>;
|
||||
using vvii = vector<vii>;
|
||||
using vviii = vector<viii>;
|
||||
using vb = vector<bool>;
|
||||
using vvb = vector<vb>;
|
Loading…
Reference in a new issue