На форумі обговорюються лише питання, пов'язані з олімпіадою
Ви не зайшли.
И всё же в этом году я возьму на себя смелость разобрать некоторые задачи и с третьего тура
Прошлый год в этом плане был для меня не очень удачным, т.к. тогда я полностью не решил ни одной задачи. В этом же году ситуация немного другая. Мною были полностью решены все задачи, кроме Rook.
1. Bracket.
Для начала заметим, что нам лучше рассматривать эту задачу не с той стороны, что мы будем удалять, а что нам надо оставить. Это позволит изменить задачу на такую: "Сколько подпоследовательной данной последовательности сами являются правильными скобочными последовательностями?". При этом надо заметить, что подпоследовательности, отличающиеся только начальными номерами входящих в них символов считаются различными. Применяем идею динамического программирования. Будем вести двумерный массив DP[i][j], где i - номер символа, к которому мы подошли, а j - количество незакрытых скобок, которые мы можем к этому моменту иметь. DP[i][j] - количество различных подпоследовательностей, последний символ которых имеет индекс <=i и при этом в них имеется ровно j открытых скобок. Понятно, что по окончанию работы нашей программы ответ будет лежать в ячейке DP[n][0]. Теперь пройдёмся в цикле по нашей строке. Рассмотрим, как мы можем повлиять на значения в DP если мы возьмём символ i. Пусть a[i]=='('. Тогда мы сможем увеличить кол-во открытых скобок в любой последовательности на 1. Если же a[i]==')', то мы напротив сможем уменьшить кол-во открытых скобок на 1 (если их не 0). Если же a[i]==']', то мы можем свести кол-во открытых скобок в 0 (если их не 0). Объединяем это всё и получаем решение.
Наилучшая ассимптотика: O(n^2).
Код:
int DP[800][800]; main() { char s[800]; cin>>s; int n=strlen(s); DP[0][0]=1; for(int i=1;i<=n;i++) for(int j=0;j<i;j++) {DP[i][j]=(DP[i][j]+DP[i-1][j])%1000007; if(s[i-1]==']' && j) DP[i][0]=(DP[i][0]+DP[i-1][j])%1000007; if(s[i-1]==')' && j) DP[i][j-1]=(DP[i][j-1]+DP[i-1][j])%1000007; if(s[i-1]=='(') DP[i][j+1]=(DP[i][j+1]+DP[i-1][j])%1000007;} cout<<DP[n][0]<<endl; }
2. TETRIS365.
Я не знаю, как это доказать, но такая идея оказалась правильной:
Если n нечётное - то ответ 0 (это доказывается раскраской фигур в шахматном порядке). Иначе пройдёмся по всем делителям n*4*5 (площадь полученной фигуры) за O(sqrtn). Утверждается, что любой прямоугольник, одна из сторон которого больше, чем 2 можно замостить.
Наилучшая ассимптотика: O(sqrtn)
Код:
main() { int n; cin>>n; if(n%2) {cout<<0<<endl;return 0;} int S=n*4*5; int s=0; for(int i=3;i*i<=S;i++) if(!(S%i)) s++; cout<<s<<endl; }
3. Traceroute.
Мне так и не удалось найти ассимптотически оптимальное решение этой задачи. Однако, я порасчитывал на то, что смогу очень-очень хорошо модифицировать неоптимальное, чтобы оно набрало как можно больше баллов. К моему превеликому удивлению оно действительно набрало много баллов. 60/60 . Моя идея заключалась в том, чтобы сначала дважды пройтись по графу алгоритмом Дейкстры - из начальной и из конечной точек, а затем использовать полученные данные в качестве эвристики для алгоритма А*. Кроме того, в моём решении использовалась структура данных "система непересекающихся множеств", которая позволяла за O(n) узнавать, будет ли для данного запроса ответ равен -1. Подробнее можно посмотреть в коде - я снабдил его множеством комментариев.
Наилучшая ассимптотика: O(mnlogn)
Код:
#define INF 1000000000 struct DSU { int *parent; int *size; void make_set(int x) {parent[x]=x; size[x]=1;} DSU(int n) {parent=new int[n]; size=new int[n]; for(int i=0;i<n;i++) make_set(i);} DSU(DSU t,int n) {parent=new int[n]; size=new int[n]; for(int i=0;i<n;i++) parent[i]=t.parent[i], size[i]=t.size[i];} int find_set(int x) { if(parent[x]==x) return x; else return parent[x]=find_set(parent[x]); } void union_set(int a,int b) { a=find_set(a); b=find_set(b); if(a!=b) { if(size[a]<size[b]) swap(a,b); parent[b]=a; size[a]+=size[b]; } } }; int n=1000; int mat[2000][2000]; int G[2000][2000]; int path[2][2000][2000]; bool good[2000*2000]; main() { ios::sync_with_stdio(0); /*--------- Считывание графа ----------*/ int a,b; cin>>n>>a>>b; int sz[n]; fill(sz,sz+n,0); fill(good,good+n*n,1); for(int i=0;i<n;i++) for(int j=0;j<n;j++) { cin>>mat[i][j]; if(!mat[i][j]) mat[i][j]=INF,good[min(i,j)*n+max(i,j)]=0; else G[i][sz[i]++]=j; } int m; cin>>m; int bad[m-1]; int divn[m-1]; int modn[m-1]; int x,y; cin>>x; x--; for(int i=1;i<m;i++) { cin>>y; y--; bad[i-1]=min(x,y)*n+max(x,y); divn[i-1]=min(x,y); modn[i-1]=max(x,y); good[min(x,y)*n+max(x,y)]=0; swap(x,y); } /*-------------------------------------*/ /*------- Препроцессинг алгоритмом ----*/ /*-------------- Дейкстры -------------*/ //int path[2][n][n]; // Рёбра, входящие в путь к начальной и конечной вершине от заданной int len[2][n]; // Длины кратчайших путей int prev[2][n]; // Предыдущие вершины в кратчайших путях int ans[2][n]; // Длина кратчайших путей bool used[2][n]; // ... for(int i=0;i<2;i++) { fill(ans[i],ans[i]+n,INF); fill(len[i],len[i]+n,0); fill(used[i],used[i]+n,0); fill(prev[i],prev[i]+n,-1); if(i) ans[i][b-1]=0; else ans[i][a-1]=0; for(int j=0;j<n;j++) { // Магия int cur=-1; for(int k=0;k<n;k++) if(!used[i][k] && (cur==-1 || ans[i][k]<ans[i][cur])) cur=k; if(ans[i][cur]==INF) break; used[i][cur]=1; for(int k=0;k<sz[cur];k++) { int cc=G[cur][k]; if(ans[i][cur]+mat[cur][cc]<ans[i][cc]) { ans[i][cc]=ans[i][cur]+mat[cur][cc]; prev[i][cc]=cur; } } } for(int j=0;j<n;j++) { // Восстанавливаем пути и превращаем их, как списки вершин в списки рёбер int k=j; while(prev[i][k]+1) path[i][j][len[i][j]++]=min(prev[i][k],k)*n+max(prev[i][k],k), k=prev[i][k]; sort(path[i][j],path[i][j]+len[i][j]); } } /*-------------------------------------*/ /*---------- Подготовка DSU -----------*/ DSU t(n); for(int j=0;j<n;j++) for(int i=0;i<j;i++) if(good[i*n+j]) t.union_set(i,j); // Добавляем все рёбра, кроме "плохих" /*-------------------------------------*/ /*----------- Поиск ответа ------------*/ int c_bad; int c_ans[n]; // Эвристика расстояния до конечной вершины от заданной int cost[n]; // Расстояние до начальной вершины от заданной int i,j; int c_a,c_b; // Количество вершин, путь в которых из первой //и из второй вершины соответственно не проходит через запретное ребро int c_len; // Длина запрещённого ребра для быстрой замены int c_num,c_cost,c_h; // Внутри цикла номер текущей вершины, //длина пути в неё из начала и эвристика длины до конца соответственно int c_ok[2][n]; int mod; set<pair<pair<int,int>,int> > que; // Ку! for(i=0;i<m-1;i++) { DSU c(t,n); for(j=0;j<m-1;j++) // Быстро (за O(m)) проверяем, будет ли вообще ответ if(i!=j) c.union_set(divn[j],modn[j]); if(c.find_set(a-1)!=c.find_set(b-1)) {cout<<-1<<' ';continue;} c_bad=bad[i]; fill(c_ans,c_ans+n,INF); fill(cost,cost,INF); c_a=0,c_b=0; for(j=0;j<n;j++) // Считаем, в какую сторону нам предположительно выгоднее искать - к концу или к началу c_a+=c_ok[1][j]=!binary_search(path[0][j],path[0][j]+len[0][j],c_bad), // Из данной вершины есть путь в начальную без плохого ребра c_b+=c_ok[0][j]=!binary_search(path[1][j],path[1][j]+len[1][j],c_bad); // Из данной вершины есть путь в конечную без плохого ребра c_len=mat[divn[i]][modn[i]]; // Удаляем плохое ребро mat[divn[i]][modn[i]]=INF; mat[modn[i]][divn[i]]=INF; if(c_ok[1][b-1]) {cout<<ans[1][a-1]<<' ';goto PERFECT;} if(c_ok[0][a-1]) {cout<<ans[0][b-1]<<' ';goto PERFECT;} que.clear(); if(c_a<c_b) mod=0,que.insert(make_pair(make_pair(ans[1][a-1],a-1),0)); else mod=1,que.insert(make_pair(make_pair(ans[0][b-1],b-1),0)); while(!que.empty()) { c_num=que.begin()->first.second; c_h=que.begin()->first.first; c_cost=que.begin()->second; que.erase(que.begin()); c_ans[c_num]=c_h; cost[c_num]=c_cost; if(c_ok[mod][c_num]) {c_ans[mod?a-1:b-1]=c_h;break;} for(j=0;j<sz[c_num];j++) { int cc=G[c_num][j]; if(c_ans[cc]>c_cost+mat[c_num][cc]+ans[!mod][cc]) { que.erase(make_pair(make_pair(c_ans[cc],cc),cost[cc])); c_ans[cc]=c_cost+mat[c_num][cc]+ans[!mod][cc]; cost[cc]=c_cost+mat[c_num][cc]; que.insert(make_pair(make_pair(c_ans[cc],cc),cost[cc])); } } } if(c_a<c_b) cout<<c_ans[b-1]<<' '; else cout<<c_ans[a-1]<<' '; PERFECT: mat[divn[i]][modn[i]]=c_len; mat[modn[i]][divn[i]]=c_len; } /*-------------------------------------*/ }
4. Garden365.
Скажу прямо - банальнейшея задача на умение пользоваться гуглом. Легко заметить, что это задача решается с помощью формулы Брахмагупты, описывающей площадь вписанного четырёхугольника. Зная расширенную формулу Брахмагупты, легко доказать, что именно у вписанного четырёхугольника площадь максимальна.
Формула:
Ассимптотика: О(1).
5. Rook.
Можно заметить, что с таким небольшим N, мы можем, применив технику сжатия координат свести нашу плоскость 1e9*1e9 к вполне себе безобидному 2000*2000. После этого можно найти ответ с помощью грамотного bfs. Моё решение даёт TLE на одном тесте (по всей видимости, в связи с большой константой) и WA на одном, набирая 54/60 баллов. Немного позднее я постараюсь разобраться с причинами неправильного ответа и сообщу о результатах. Пока что предоставлю код.
Наилучшая ассимптотика: O(n^2)
Код:
#define INF 1000000000 struct point {int x,y; point(){} point(int a,int b){x=a,y=b;} }; main() { ios::sync_with_stdio(0); // Считывание данных int x1,y1,x2,y2; cin>>x1>>y1>>x2>>y2; int n; cin>>n; point coords[n+4]; for(int i=0;i<n;i++) cin>>coords[i].x>>coords[i].y; coords[n].x=x1; coords[n].y=y1; coords[n+1].x=x2; coords[n+1].y=y2; coords[n+2].x=-2000000007; coords[n+2].y=-2000000007; coords[n+3].x=2000000007; coords[n+3].y=2000000007; int X[n+4],Y[n+4]; for(int i=0;i<n+4;i++) X[i]=coords[i].x,Y[i]=coords[i].y; sort(X,X+n+4); sort(Y,Y+n+4); // Сжатие координат map<int,int> new_coord_x,new_coord_y; int curx=0,cury=0; new_coord_x[X[0]]=0,new_coord_y[Y[0]]=0; for(int i=1;i<n+4;i++) { if(X[i]-X[i-1]==1) curx++; else if(X[i]-X[i-1]>1) curx+=2; if(Y[i]-Y[i-1]==1) cury++; else if(Y[i]-Y[i-1]>1) cury+=2; new_coord_x[X[i]]=curx, new_coord_y[Y[i]]=cury; } // Создание карты местности int MAP[curx+1][cury+1][4]; int ans[curx+1][cury+1][4]; for(int i=0;i<=curx;i++) for(int j=0;j<=cury;j++) for(int k=0;k<4;k++) MAP[i][j][k]=1, ans[i][j][k]=INF; for(int i=0;i<n;i++) for(int j=0;j<4;j++) MAP[new_coord_x[coords[i].x]][new_coord_y[coords[i].y]][j]=0; x1=new_coord_x[x1]; y1=new_coord_y[y1]; x2=new_coord_x[x2]; y2=new_coord_y[y2]; for(int i=0;i<4;i++) ans[x1][y1][i]=0, MAP[x1][y1][i]=1; // Поиск в ширину по сжатым координатам deque<pair<point,int> > que; que.push_back(make_pair(point(x1,y1),4)); int dx[]={1,0,-1,0}; int dy[]={0,1,0,-1}; while(!que.empty()) { point cur=que[0].first; int x=cur.x; int y=cur.y; int to=que[0].second; que.pop_front(); if(to!=4) {if(!MAP[x][y][to]) continue; MAP[x][y][to]=0;} for(int i=0;i<4;i++) { if(x+dx[i]>=0 && x+dx[i]<=curx && y+dy[i]>=0 && y+dy[i]<=cury && MAP[x+dx[i]][y+dy[i]][i]) { if(i==to) { que.push_front(make_pair(point(x+dx[i],y+dy[i]),i)); ans[x+dx[i]][y+dy[i]][i]=ans[x][y][i]; } else if(MAP[x+dx[i]][y+dy[i]][i]==1) { que.push_back(make_pair(point(x+dx[i],y+dy[i]),i)); MAP[x+dx[i]][y+dy[i]][i]=2; if(to==4) ans[x+dx[i]][y+dy[i]][i]=1; else ans[x+dx[i]][y+dy[i]][i]=ans[x][y][to]+1; } } } } int ANS=*min_element(ans[x2][y2],ans[x2][y2]+4); if(ANS==INF) cout<<-1<<endl; else cout<<ANS<<endl; }
На этом всё. Всем, кто пройдёт, удачи на четвёртом туре! Пусть победит сильнейший и всё такое
P.S. У меня сегодня День Рождения, мне исполнилось 18 лет
Відредаговано adamant (2014-02-07 10:44:11)
Поза форумом
Очень хотелось бы услышать комментарий жюри по поводу теста к задаче Garden365, который никто не смог сдать
Поза форумом
Вот такой тест у меня не проходит:
1.00000000000000000000000001 1 1 3
Возможно нужна была длинная арифметика
Відредаговано traveller6 (2014-02-07 11:31:13)
Поза форумом
В задаче Traceroute только в трёх тестах n>=100 и ни в одном n>=200.
Это при ограничениях в N<=2000 Oo
Поза форумом
traveller6 написав:
Вот такой тест у меня не проходит:
1.00000000000000000000000001 1 1 3
Так чтобы такой вход хранить, точности long double не хватает. От нас что, длинную арифметику тут ждали?
Поза форумом
Кроме длинной арифметики тут больше по-моему ничего не придумаешь.
Відредаговано traveller6 (2014-02-07 11:36:03)
Поза форумом
В условии вообще ничего не намекало на неё, особенно учитывая то, что точность вывода в примере стоит 8 знаков.
Відредаговано adamant (2014-02-07 11:38:37)
Поза форумом
Возможно ничего не намекало на длинную арифметику потому, что автроры хотели проверить нашу внимательность/находчивость/или просто ради смеху. Тем более, что за это давали всего 3 балла. Да и задача получилась подорзрительно легкая. По-моему то, что все получили на 3 балла меньше вполне справедливо.
Відредаговано traveller6 (2014-02-07 11:43:47)
Поза форумом
Кстати, в Traceroute, как оказалось, проходил и обычный вызов Дейкстры после каждого удаления. Вот умора-то.
И да, странная проверка находчивости - дать человеку лопату, сказать копать яму и ожидать, пока он для этих целей сделает из лопаты экскаватор...
Поза форумом
Хм, перетестирование пошло Оо
Поза форумом
Ну вот, версия про длинную арифметику и отпала
Поза форумом
adamant написав:
В задаче Traceroute только в трёх тестах n>=100 и ни в одном n>=200.
Это при ограничениях в N<=2000 Oo
Откуда это вам известно?
Поза форумом
В дорешивании немного поразвлекался с дописыванием while(n>=x); в код.
Поза форумом
Никто не подскажет, что за проблема с 10 тестом в последней задаче? Ни у кого такого не было? У меня в ней сжатие координат + 0-1bfs.
Поза форумом
У меня WA3, TLE 9, ничем не могу помочь
Поза форумом
Тем временем Traceroute перепроверили.
Ну ладно, а у кого больше, чем 48 баллов по нему - как решали-то?
Відредаговано adamant (2014-02-07 19:27:16)
Поза форумом
Подведены окончательные итоги 3-го тура и всех 3-х туров сумарно. Не будем отступать от традиций - в финал проходят все, кто набрал 200+ баллов, принося с собой 1/10 от набранной суммы в первых 3-х турах. Таких около 180. Ждите информацию по почте!
Поза форумом
adamant написав:
Тем временем Traceroute перепроверили.
Ну ладно, а у кого больше, чем 48 баллов по нему - как решали-то?
Эффективным алгоритмом Дейкстры с очередью по приоритетом или Левитом e-maxx.ru
Поза форумом
Жюри_Непомнящий написав:
adamant написав:
Тем временем Traceroute перепроверили.
Ну ладно, а у кого больше, чем 48 баллов по нему - как решали-то?Эффективным алгоритмом Дейкстры с очередью по приоритетом или Левитом e-maxx.ru
Если вызывать его каждый раз - это даст ассимптотику O(mnlogn), что для полного графа с 2000 вершин выльется в n^3*logn, что даже хуже, чем если запускать "обычный" алгоритм Дейкстры. Кроме того, в моём решении используется алгоритм А* на контейнере set, что даёт в худшем случае такую же ассимптотику, как и Дейкстра (т.к. A* по сути является его модификацией), но в то же время ещё и значительно улучшает время работы в среднем за счёт множества отсечений. Алгоритм Левита с e-maxx, как было выяснено на codeforces работает в худшем случае за экспоненциальное время, что превращает его использование на соревнованиях в азартную игру - угадай, поставят ли авторы тест, который заставляет его работать по-настоящему медленно.
И да, я правильно понял - в авторском решении алгоритм Дейкстры запускается КАЖДЫЙ РАЗ при удалении ребра? Или всё же он запускается вначале, а затем пересчёт ответа на запрос делается за время меньшее, чем O(mlogn)?
Відредаговано adamant (2014-02-07 21:33:33)
Поза форумом
#include <iostream> using namespace std; int main() { long long k; // Количество комплектов (вводимые данные) int v; // Количество полученных прямоугольников (выводимые данные) long long m; // Временная переменная, которая хранит минимальное значение целочисленного деления cin >> k; v = 0; if ((k % 2) == 0) { k *= 20; m = k; for (long long i = 3; (i <= 10^12) && (m > i); i++) { if ((k % i) == 0) { m = k / i; v++; //cout << "i = " << i << " m = " << m << " k = " << k << " v = " << v << endl; } } } cout << v << endl; return 0; }
Извините, но у меня стоит 57, хотя проходит на 60, в чем проблема???
Поза форумом
adamant написав:
Жюри_Непомнящий написав:
adamant написав:
Тем временем Traceroute перепроверили.
Ну ладно, а у кого больше, чем 48 баллов по нему - как решали-то?Эффективным алгоритмом Дейкстры с очередью по приоритетом или Левитом e-maxx.ru
Если вызывать его каждый раз - это даст ассимптотику O(mnlogn), что для полного графа с 2000 вершин выльется в n^3*logn, что даже хуже, чем если запускать "обычный" алгоритм Дейкстры. O(mlogn)?
Дейскстра пропорциональна квадрату, а не как вы сказали - "обычный" алгоритм Дейкстры. O(mlogn)
Відредаговано LeonID (2014-02-07 23:05:47)
Поза форумом
LeonID написав:
Дейскстра пропорциональна квадрату, а не как вы сказали - "обычный" алгоритм Дейкстры. O(mlogn)
Э... Я не понял, о чём это сообщение. Я написал о том, что в случае полного графа Дейкстра с приоритетной очередью даст при кратчайшем пути из n вершин итоговую ассимптотику порядка n^3logn, а квадратичный алгоритм - n^3.
Поза форумом
adamant написав:
4. Garden365.
Скажу прямо - банальнейшея задача на умение пользоваться гуглом. Легко заметить, что это задача решается с помощью формулы Брахмагупты, описывающей площадь вписанного четырёхугольника. Зная расширенную формулу Брахмагупты, легко доказать, что именно у вписанного четырёхугольника площадь максимальна.
Формула:
http://upload.wikimedia.org/math/2/e/7/ … e68247.png
Ассимптотика: О(1).
какой угол ты добавлял в формулу ?
Поза форумом
То есть в тестах к задаче Traceroute, оказывается, ребер меньше, чем n^2? Но это нигде не указано, на это даже нету намеков..
Поза форумом
IGOS написав:
adamant написав:
4. Garden365.
Скажу прямо - банальнейшея задача на умение пользоваться гуглом. Легко заметить, что это задача решается с помощью формулы Брахмагупты, описывающей площадь вписанного четырёхугольника. Зная расширенную формулу Брахмагупты, легко доказать, что именно у вписанного четырёхугольника площадь максимальна.
Формула:
http://upload.wikimedia.org/math/2/e/7/ … e68247.png
Ассимптотика: О(1).какой угол ты добавлял в формулу ?
Вписанному четырёхугольнику соответствует угол в 90 градусов. В таких условиях вычитаемое вырождается в ноль.
Да, тета - полусумма противолежащих углов.
Відредаговано adamant (2014-02-08 07:36:02)
Поза форумом