#include #include #include #include #include #include #include #include using namespace std; typedef long long llong; bool LOCAL_RUN = false; const double TIME_LIMIT = 3.0; const double EPS = 1e-1; bool TimeIsOK(double limit = TIME_LIMIT) { return (double)clock() / (double)CLOCKS_PER_SEC < limit; } inline double fabs(double a) { if (a < 0.0) return -a; else return a; } inline bool eq(double a, double b) { return fabs(a - b) < EPS; } inline int sq(int a) { return a * a; } inline double sq(double a) { return a * a; } const llong SEED = 1337LL; mt19937 mtn(SEED); inline int mt() { int v = abs(mtn()); if (v < 0) return abs(v+1); else return v; } void resetRandom() { mt19937 z(SEED); mtn = z; } llong bigr() { return (llong)mt() * (llong)mt() + (llong)mt(); } struct dcol { double r, g, b; dcol(): r(0), g(0), b(0) {} dcol(double a, double b, double c): r(a), g(b), b(c) {} dcol operator+(const dcol &other) const { return dcol(r + other.r, g + other.g, b + other.b); } dcol operator/(const double v) const { return dcol(r / v, g / v, b / v); } dcol operator/(const int v) const { return (*this) / (double)v; } dcol operator*(const double v) const { return (*this) / (1.0 / v); } bool operator==(const dcol &other) const { return eq(r, other.r) && eq(g, other.g) && eq(b, other.b); } }; struct col { int r, g, b; col(): r(0), g(0), b(0) {} col(int a, int b, int c): r(a), g(b), b(c) {} static col fromDouble(dcol c) { return col(round(c.r), round(c.g), round(c.b)); } static col randomCol() { return col(mt() % 256, mt() % 256, mt() % 256); } static col mix(col &a, col &b) { return fromDouble((a.toDouble() + b.toDouble()) / 2); } dcol toDouble() { return dcol(r, g, b); } bool operator<(const col &other) const { return make_pair(r, make_pair(g, b)) < make_pair(other.r, make_pair(other.g, other.b)); } bool operator<=(const col &other) const { return make_pair(r, make_pair(g, b)) <= make_pair(other.r, make_pair(other.g, other.b)); } bool operator==(const col &other) const { return r == other.r && g == other.g && b == other.b; } col operator+(const col &other) const { return col(r + other.r, g + other.g, b + other.b); } }; int colDist(col &a, col &b) { return sq(a.r - b.r) + sq(a.g - b.g) + sq(a.b - b.b); } double colDist(dcol &a, dcol &b) { return sq(a.r - b.r) + sq(a.g - b.g) + sq(a.b - b.b); } template vector< vector > rot(vector< vector > g) { vector< vector > res; int n = g.size(), m = g[0].size(); int i,j; res.resize(m); for (i=0;i=0;j--) { res[i].push_back(g[j][i]); } } return res; } struct compressorResult { vector palette; vector< vector > rawColours; void print() { int i, j; for (i=0;i > img; void initKmeans() { int i, j, in; initmeans[0] = pts[mt() % pL]; kmeans[0] = initmeans[0].toDouble(); for (i=1;i sample) { choice = j; break; } sample -= (llong)minDistances[j]; } if (choice == -1) { initmeans[i] = initmeans[i-1]; } else { initmeans[i] = pts[choice]; } kmeans[i] = initmeans[i].toDouble(); } } void kmeansPalette() { bool changes = true; int i, j; double bestCost = -1.0; while(TimeIsOK()) { int iterations = 0; changes = true; initKmeans(); double lastScore = -1.0; while(TimeIsOK() && changes) { iterations++; changes = false; for (i=0;i 0.0) nextGuess[i] = nextGuess[i] / groupCount[i]; if (groupCount[i] > 0) { if (!eq(kmeans[i].r, nextGuess[i].r) || !eq(kmeans[i].g, nextGuess[i].g) || !eq(kmeans[i].b, nextGuess[i].b)) changes = true; kmeans[i] = nextGuess[i]; } } if (iterations > 1) { if ( fabs((score-lastScore)/lastScore*100.0) < 0.1 ) changes = false; } lastScore = score; } fprintf(stderr, "K-Means converged in %d iterations\n", iterations); for (i=0;i to56(col c) { int num256 = c.r * 256 * 256 + c.g * 256 + c.b; vector b56; int i; for (i=1;i<=5;i++) { b56.push_back(num256 % 56); num256 /= 56; } reverse(b56.begin(), b56.end()); return b56; } void pasteColors(int x, int y, int code = 15) { int i, j, in, ja; assert( (x + y) % 2 == 0 ); int curc = 16; vector colorCode = to56(palette[curc]); int colorPart = 0; vector< pair > tiles; for (i=x;i= K) break; if (i == x && j == y) { rawColors[i][j] = 7; rawColors[i][j+1] = code; } else { int v = colorCode[colorPart]; int v1 = v / 8, v2 = v % 8 + 8; rawColors[i][j] = v1; rawColors[i][j+1] = v2; tiles.push_back(make_pair(v1, v2)); colorPart++; } if (colorPart == colorCode.size()) { colorPart = 0; curc++; if (curc < K) colorCode = to56(palette[curc]); } } } assert(curc >= K); int tp = 0; for (i=x;i= tiles.size()) break; rawColors[i+1][j+1] = tiles[tp].first; rawColors[i+1][j+2] = tiles[tp].second; tp++; } } } compressorResult colorAugmentation(bool rotation, int kAdvice = -1) { if (kAdvice < 0) K = PS; else K = kAdvice; if (rotation) { img = rot(img); n = img.size(); m = img[0].size(); } int i, j, in, ja, i1, j1; int a, b, c; fprintf(stderr, "Color augmentation compression of picture size %d x %d [%d]\n", n, m, n*m); map seenPt; map::iterator it; pL = 0; for (i=0;i row; for (j=0;j seenPt; map::iterator it; pL = 0; for (i=0;i row; for (j=0;j= 4 * eM * 2 + 4 && N >= 2 * eN * 2 + 4; } const int TOLERANCE = 2000; pair easyEstimate() { int i,j,in; vector basis; llong easyEstimate = 0; for (i=0;i TOLERANCE) { basis.push_back(img[i][j]); } else easyEstimate += (llong)minDist; } } } fprintf(stderr,"Easy estimate: %lld\n", easyEstimate); return make_pair((int)basis.size(), easyEstimate); } int blendCost(col A, col B) { col mix = col::mix(A, B); return colDist(A, mix) + colDist(B, mix); } pair augEstimates() { int i,j; llong estH = 0; for (i=0;i cs; cs.push_back(img[i][j]); cs.push_back(img[i+1][j]); cs.push_back(img[i][j+1]); cs.push_back(img[i+1][j+1]); int cost = blendCost(cs[0], cs[1]) + blendCost(cs[2], cs[3]); cost = min(cost, blendCost(cs[0], cs[2]) + blendCost(cs[1], cs[3])); cost = min(cost, blendCost(cs[0], cs[3]) + blendCost(cs[1], cs[2])); mixedEst += cost; } if (m % 2 == 1) mixedEst += colDist(img[i][m-1], img[i][m-2]); } if (n % 2 == 1) { for (i=0;i > &rimg) { img = rimg; n = img.size(); m = img[0].size(); pair easyEst = easyEstimate(); int colorFactor = easyEst.first; pair augEst = augEstimates(); if (!augFits(n, m)) augEst.first = 1000000000LL; if (!augFits(m, n)) augEst.second = 1000000000LL; fprintf(stderr, "Color factor: %d\n", colorFactor); double goalFrac = 1.0; if (colorFactor <= 10) { goalFrac = 100.0; } else if (colorFactor < 33) { goalFrac = 1.0 + 2 * ((33.0 - colorFactor) / 22.0); } if (n < 100 || m < 100 || n * m < 30000) { return easyColor(); } else { double frac = (double)easyEst.second / (double)min(augEst.first, augEst.second); if (frac < goalFrac) return easyColor(); else { if (augEst.first <= augEst.second) return colorAugmentation(false); else return colorAugmentation(true); } } } }; struct decompressorResult { vector< vector > img; void print() { int i, j; for (i=0;i palette; vector< vector > img; vector< vector > raw; col tmp[511][511]; bool displaced[511][511]; int dispNbs[511][511]; vector< pair > nbs[511][511]; vector< pair > component; bool TFO[511][511]; void getComponent(int x, int y) { if (x < 0 || y < 0 || x >= n || y >= m) return; if (TFO[x][y] || !displaced[x][y]) return; TFO[x][y] = true; component.push_back(make_pair(x, y)); getComponent(x+1, y); getComponent(x-1, y); getComponent(x, y+1); getComponent(x, y-1); } vector cmpSizes; int matched[250111]; int usualMatch[250111]; int usualValue[250111]; void batrak(int ind) { int i; if (ind == component.size()) { for (i=0;i v = component[i]; displaced[v.first][v.second] = false; raw[v.first][v.second] = usualValue[i]; } } component.clear(); } void buildCertainMatches() { int i,j; memset(TFO,false,sizeof(TFO)); for (i=0;i > toMatch; bool negated = false; bool isStart(int x, int y) { return ((x + y) % 2 == 0) != negated; } void fixShuffle() { int i, j, in, ja; int disbalance = 0; for (i=0;i= n || y >= m) continue; if (displaced[x][y]) { dispNbs[i][j]++; nbs[i][j].push_back(make_pair(x, y)); } } } if (dispNbs[i][j] == 1) toMatch.push_back(make_pair(i, j)); assert(dispNbs[i][j] > 0); } } fprintf(stderr, "%d colors displaced\n", displacedCtr); while(!toMatch.empty()) { int x = toMatch.back().first, y = toMatch.back().second; toMatch.pop_back(); if (!displaced[x][y]) continue; int xm = -1, ym = -1; for (i=0;i= 0 && y >= 0 && x < n && y < m; } vector< vector > paletteParts; vector< pair > colorInfo; int allowedOutside; int failures = 0; void processBarcode(int x, int y, bool forceInside = false) { int i, j; if (x >= 1 && y >= 1 && x < n - 1 && y < m - 1) { bool good = false; for (i=-1;i<=1;i++) { for (j=-1;j<=1;j++) { if (raw[x+i][y+j] == 7) good = true; } } if (!good) return; } else if (allowedOutside == 0 || forceInside) return; for (i=0;i= 0 && x < n && y >= 0 && y < n) displaced[x][y] = true; if (x >= 0 && x < n && y+1 >= 0 && y+1 < n) displaced[x][y+1] = true; for (i=x;i= K) break; if (inside(i, j) && inside(i, j+1)) { int v = raw[i][j] * 8 + raw[i][j+1] - 8; if (!displaced[i][j] && !displaced[i][j+1]) { paletteParts[curc][parts] = v; } } if (inside(i, j)) displaced[i][j] = true; if (inside(i, j+1)) displaced[i][j+1] = true; parts++; if (parts == 5) { parts = 0; curc++; } } } curc = 16; parts = 0; for (i=x;i= K) break; if (inside(i+1, j+1) && inside(i+1, j+2)) { int v = raw[i+1][j+1] * 8 + raw[i+1][j+2] - 8; if (!displaced[i+1][j+1] && !displaced[i+1][j+2]) { paletteParts[curc][parts] = v; } } if (inside(i+1, j+1)) displaced[i+1][j+1] = true; if (inside(i+1, j+2)) displaced[i+1][j+2] = true; parts++; if (parts == 5) { parts = 0; curc++; } } } } void generateOthers(pair A, pair B) { if (A.first != B.first && A.second != B.second) /// 1, 4 { processBarcode(A.first, B.second, true); processBarcode(B.first, A.second, true); processBarcode(A.first, B.second); processBarcode(B.first, A.second); } else if (A.first == B.first) ///2, 6 { processBarcode(A.first + 2*eN + 2, A.second, true); processBarcode(B.first + 2*eN + 2, B.second, true); processBarcode(A.first - 2*eN - 2, A.second, true); processBarcode(B.first - 2*eN - 2, B.second, true); processBarcode(A.first + 2*eN + 2, A.second); processBarcode(B.first + 2*eN + 2, B.second); processBarcode(A.first - 2*eN - 2, A.second); processBarcode(B.first - 2*eN - 2, B.second); } else ///3, 5 { processBarcode(A.first, A.second + 4*eM + 2, true); processBarcode(B.first, B.second + 4*eM + 2, true); processBarcode(A.first, A.second - 4*eM - 2, true); processBarcode(B.first, B.second - 4*eM - 2, true); processBarcode(A.first, A.second + 4*eM + 2); processBarcode(B.first, B.second + 4*eM + 2); processBarcode(A.first, A.second - 4*eM - 2); processBarcode(B.first, B.second - 4*eM - 2); } } void generateFromSingle(pair A) { processBarcode(A.first - 2*eN - 2, A.second, true); processBarcode(A.first + 2*eN + 2, A.second, true); processBarcode(A.first, A.second - 4*eM - 2, true); processBarcode(A.first, A.second + 4*eM + 2, true); } void learnColors() { int i, j, in, ja; paletteParts.resize(palette.size()); for (i=0;i= 2) { generateOthers(colorInfo[0], colorInfo[1]); } for (i=16;i q[250111]; int qL = 0; bool inq[511][511]; int imgColCtr[511][511]; void fillDisplacements() { int uk = 0; int i, j; for (i=0;i 0); img[x][y] = col::fromDouble(img[x][y].toDouble() / imgColCtr[x][y]); for (i=-1;i<=1;i++) { for (j=-1;j<=1;j++) { if (abs(i) + abs(j) != 1) continue; int nx = x + i, ny = y + j; if (nx < 0 || ny < 0 || nx >= n || ny >= m) continue; if (!displaced[nx][ny]) continue; if (!inq[nx][ny]) { q[qL] = make_pair(nx, ny); qL++; inq[nx][ny] = true; } img[nx][ny] = img[nx][ny] + img[x][y]; imgColCtr[nx][ny]++; } } uk++; } } int rawImageView[511][511]; decompressorResult colorAugmentation() { int i, j, in, ja; bool rotation = (palette[3] < palette[2]); if (rotation) { fprintf(stderr, "ROTATION!\n"); raw = rot(raw); } n = raw.size(); m = raw[0].size(); palette.resize(PS); fprintf(stderr,"Color Augmentation decompressing %d x %d [%d]\n", n, m, n*m); fixShuffle(); learnColors(); fprintf(stderr,"Shuffle fixed\n"); img.resize(n); for (i=0;i row; for (j=0;j= n || y >= m) continue; if (rawImageView[x][y] != rawImageView[i][j]) diffs++; ctr++; mnc = mnc + img[x][y]; } } mnc = col::fromDouble(mnc.toDouble() / ctr); if (diffs >= 2 && !unusable) { double selfWeight = 0.5; dcol c = (mnc.toDouble() * (1.0 - selfWeight)) + (img[i][j].toDouble() * selfWeight); row.push_back(col::fromDouble(c)); } else row.push_back(img[i][j]); } res.img.push_back(row); } } else { for (i=0;i row; for (j=0;j row; for (j=0;j row; for (j=0;j= n || y >= m) continue; if (raw[x][y] != raw[i][j]) diffs++; ctr++; mnc = mnc + img[x][y]; } } mnc = col::fromDouble(mnc.toDouble() / ctr); if (diffs >= 2) { double selfWeight = 0.5; dcol c = (mnc.toDouble() * (1.0 - selfWeight)) + (img[i][j].toDouble() * selfWeight); row.push_back(col::fromDouble(c)); } else row.push_back(img[i][j]); } res.img.push_back(row); } return res; } public: decompressorResult decompress(vector &rawpalette, vector< vector > &rawinp) { raw = rawinp; palette = rawpalette; if (palette[0] <= palette[1]) { return colorAugmentation(); } else { return easyColor(); } } }; vector< vector > readRun0() { vector< vector > res; int n,m; int i,j; scanf("%d %d",&m,&n); for (i=0;i row; for (j=0;j readPalette() { int i; vector palette; for (i=0;i<16;i++) { int a,b,c; scanf("%d %d %d",&a,&b,&c); palette.push_back(col(a,b,c)); } return palette; } vector< vector > readRawImg() { int i, j; int n, m; vector< vector > res; scanf("%d %d",&m,&n); for (i=0;i row; for (j=0;j, pair > getWindow(int n, int m) { int vertLimit = n / 5; int horizLimit = m / 5; return make_pair( make_pair(mt() % vertLimit, mt() % horizLimit), make_pair(n - 1 - mt() % vertLimit, m - 1 - mt() % horizLimit) ); } bool swapped[511][511]; vector< vector > cutShuffle(vector< vector > &raw, pair< pair, pair > window) { memset(swapped, false, sizeof(swapped)); vector< vector > res; int i, j; for (i=window.first.first;i<=window.second.first;i++) { vector row; for (j=window.first.second;j<=window.second.second;j++) { row.push_back(raw[i][j]); } res.push_back(row); } int n = res.size(), m = res[0].size(); int swaps = (n * m) / 10; while(swaps > 0) { int x = mt() % n, y = mt() % m; int bx = x, by = y; int g = mt() % 4; if (g == 0) x++; else if (g == 1) x--; else if (g == 2) y++; else y--; if (x < 0 || y < 0 || x >= n || y >= m) continue; if (swapped[x][y] || swapped[bx][by]) continue; swap(res[x][y], res[bx][by]); swapped[x][y] = true; swapped[bx][by] = true; swaps--; } return res; } void analyze(vector< vector > &img, compressorResult &cRes, decompressorResult &dRes, pair< pair, pair > window) { int i,j; fprintf(stderr,"-----ANALYZE-----\n"); fprintf(stderr, "Cut to %d, %d to %d, %d\n", window.first.first, window.first.second, window.second.first, window.second.second); int score = 0; for (i=0;i > img = readRun0(); Compressor C; compressorResult cRes = C.compress(img); resetRandom(); pair< pair, pair > window = getWindow(cRes.rawColours.size(), cRes.rawColours[0].size()); vector< vector > noisy = cutShuffle(cRes.rawColours, window); Decompressor D; decompressorResult dRes = D.decompress(cRes.palette, noisy); analyze(img, cRes, dRes, window); } else { if (run == 0) { Compressor C; vector< vector > img = readRun0(); C.compress(img).print(); } else { Decompressor D; vector p = readPalette(); vector< vector > raw = readRawImg(); D.decompress(p, raw).print(); } } return 0;