На форумі обговорюються лише питання, пов'язані з олімпіадою
Ви не зайшли.
Ось, кому цікаво, загальна таблиця по 3 турам:
Загальний залік
По школярам
Зараз буде код задач. Коментарі будуть дуже стислі. Можливо, пізніше трохи доповню.
Спочатку три повнобальні, потім інші.
Like3
Вся ідея розв’язку, в принципі, описується формулами з коду.
#include <iostream> #include<cstring> using namespace std; typedef long long ll; typedef unsigned long long ull; typedef unsigned ul; typedef long double ld; ull fast_pow(ull x,ull power) { ull res=1; while(power) { if(power%2)res*=x; x*=x; power>>=1; } return res; } int countL3(int n) //Рішення для одноцифрових випадків { if(n>=9)return 3; if(n>=3)return 2; if(n>=1)return 1; return 0; } ull like3(char* a,int t,bool first=true) //а - масив цифр числа, t - довжина числа { if(t<=0)return 0; if(t==1)return countL3(a[0]); ull res=(countL3(a[0]-1))*fast_pow(3,t-1); //Кількість триподібних такої-ж довжини, як і початкове число, у яких старший розряд менший, ніж у даного int i=1; if(first)while(t-i)res+=fast_pow(3,t-i),i++;//Якщо функція викликана не рекурсивно, то до розв’язку додаємо кількість триподібних, коротших за дане число if(9%a[0])return res; //Якщо перша цифра числа не 3-подібна, то відповідь уже знайдено if(a[1])res+=like3(a+1,t-1,false); //Інакше до відповіді треба додати рішення для усих цифр числа, крім першої(за умови, що друга цифра - не нуль) return res; } ull solve(char* a, char *b) //Функція, що перетворюе рядок ANSI-символів у масив "звичайних" цифр, { //Вирішує задачу окремо для першого та другого числа, а потім повертає відповідь. int a_len=strlen(a),b_len=strlen(b),i=0; bool f=true; while(a[i])a[i]-='0',f=f&&a[i]!=0&&!(9%a[i]),i++; i=0; while(b[i])b[i]-='0',i++; return like3(b,b_len)-like3(a,a_len)+f; } int main() { char a[42],b[42]; cin>>a>>b; cout<<solve(a,b)<<endl; }
Diiders
Простий перебір степеней перших 16(підібрано експериментально, з невеликим запасом) простих чисел.
#include <iostream> using namespace std; typedef long long ll; typedef unsigned long long ull; typedef unsigned ul; typedef long double ld; ull solve(ull n,ull cur_max=1, int start_prime=0) { static const ull primes[]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131}; static const int N_PRIMES=16; static const ld EPS=1e-6; static ul times_used[N_PRIMES]={0}; static ull max=0; for(int i=start_prime;i<N_PRIMES;++i) { if((ld)primes[i]*cur_max-(ld)n>EPS) { ull ts=1; for(int i=0;i<N_PRIMES;++i)ts*=times_used[i]+1; if(ts>max)max=ts; } else if(!i||times_used[i-1]>=times_used[i]+1) { times_used[i]++; solve(n,cur_max*primes[i],i); times_used[i]--; } } return max; } int main() { ull n; cin>>n; cout<<solve(n)<<endl; }
Railway
В принципі, рішення - модифікація BFS(але пошук ведеться одночасно з усіх листків, а не з якоїсь однієї вершини). Усе інше, знову ж таки, описується формулами з коду.
По часу роботи - окрема історія. По перше, класи iostream повільніші за scanf(), printf(). Але це, в принципі, не новина.
Найбільше ж на час впливало використання списків std::list. З ними, як уже раніше було з векторами, при компіляції без оптимізацій, програма працює дуже повільно. Хоча з -О2 - цілком нормальний час. Чесно кажучи, не знаю, як з цим на інших олімпіадах, але як на мене, це не дуже правильно - фактично позбавляти учасників частини стандартної бібліотеки, адже очевидно, що це сама бібліотека розраховує на подальшу оптимізацію себе компілятором. Але це все філософія, правила такі, які вони є, і приймаючи участь в олімпіаді ми з ними погоджуємося .
Тому для досягнення нормального часу роботи довелося писати свій кривий велосипед, що частково надає інтерфейс std::list(не хотілося сильно переписувати вже працюючий код самого рішення). Зі своєю реалізацією списку програма працює швидко і правильно.
ось код:
#include <queue> #include <utility> #include <cstdio> using namespace std; typedef long long ll; typedef unsigned long long ull; typedef unsigned ul; typedef long double ld; //bicycles begin template<typename _Tp> inline _Tp* __addressof(_Tp& __r) { return reinterpret_cast<_Tp*> (&const_cast<char&>(reinterpret_cast<const volatile char&>(__r))); } template<class T> struct myList { struct node { T data; node *prev, *next; node():prev(0x0),next(0x0){} }; struct iterator { node *p; T& operator *(){return p->data;} T* operator ->(){return ::__addressof(p->data);} iterator& operator ++(){p=p->next;return *this;} iterator& operator --(){p=p->prev;return *this;} bool operator ==(iterator const &rhs)const{return p==rhs.p;} bool operator !=(iterator const &rhs)const{return !(this->operator ==(rhs));} iterator():p(0x0){} }; iterator _beg; iterator _end; int _sz; iterator begin() { return _beg; } iterator end() { return _end; } int size() { return _sz; } void push_back(const T& x) { if(!_sz) { _beg.p=new node; _end.p=new node; _beg.p->prev=0x0; _end.p->next=0x0; _beg.p->next=_end.p; _end.p->prev=_beg.p; _end.p=_beg.p; } *_end=x; node* t=_end.p; _end.p->next=new node; ++_end; _end.p->prev=t; _end.p->next=0x0; _sz++; } myList():_sz(0){} iterator erase(iterator i) { node* t=i.p; if(i.p->prev)i.p->prev->next=i.p->next; else ++_beg; if(i.p->next)i.p->next->prev=i.p->prev; else --_end; ++i; delete t; _sz--; if(!_sz)i.p=0x0; return i; } }; //bicycles end struct tree { int id; pair<tree*,int> parent; myList<pair<tree*,int> > child; int max,nMax; int neighborsConsidered; bool f; tree():parent(pair<tree*,int>((tree*)0x0,-1)),max(0),nMax(0),neighborsConsidered(0),f(0){} }; inline pair<int,int> gen_tree(tree* station,int N) { int max=0, nMax=0; tree *root; queue<int> q; for(int i=0;i<N;++i) if(station[i].child.size()==1)q.push(i),station[i].nMax=1; while(!q.empty()) { int current=q.front(); q.pop(); if(station[current].f)continue; if(station[current].neighborsConsidered<(int)station[current].child.size()-1) { q.push(current); continue; } station[current].f=true; myList<pair<tree*,int> >::iterator i; for(i=station[current].child.begin();i!=station[current].child.end();++i) if(!station[i->first->id].f)break; if(i==station[current].child.end()) { root=&station[current]; station[current].parent.first=0x0; station[current].parent.second=-1; } else { station[current].parent=*i; station[current].child.erase(i); station[current].parent.first->neighborsConsidered++; q.push(station[current].parent.first->id); } int tMax=-1,nTMax=0,curMax,nCurMax,nMaxBranches=0; for(myList<pair<tree*,int> >::iterator i=station[current].child.begin();i!=station[current].child.end();++i) { if(i->first->max+i->second>station[current].max) { tMax=station[current].max; nTMax=station[current].nMax; station[current].max=i->first->max+i->second; station[current].nMax=i->first->nMax; nMaxBranches=1; } else if(i->first->max+i->second==station[current].max) station[current].nMax+=i->first->nMax,nMaxBranches++; else if(i->first->max+i->second>tMax) { tMax=i->first->max+i->second; nTMax=i->first->nMax; } else if(i->first->max+i->second==tMax) nTMax+=i->first->nMax; } if(station[current].child.size()==1)continue; if(nMaxBranches==1) { curMax=station[current].max+tMax; nCurMax=2*station[current].nMax*nTMax; } else { curMax=2*station[current].max; nCurMax=0; for(myList<pair<tree*,int> >::iterator i=station[current].child.begin();i!=station[current].child.end();++i) { if(i->first->max+i->second!=station[current].max)continue; nCurMax+=(station[current].nMax-i->first->nMax)*(i->first->nMax); } } if(curMax>max) { max=curMax; nMax=nCurMax; } else if(curMax==max) nMax+=nCurMax; } return pair<int,int>(max,nMax); } int main() { srand(time(0)); int N; scanf("%d",&N); int src,dst,price; static tree station[100000]; for(int i=0;i<N;i++) station[i].id=i; if(N==2) { int res,a; scanf("%d%d%d",&a,&a,&res); printf("%d 2\n",res); return 0; } for(int i=0;i<N-1;i++) { scanf("%d%d%d",&temp.src,&temp.dst,&temp.price); src--, dst--; station[src].child.push_back(pair<tree*,int>(&station[dst],price)); station[dst].child.push_back(pair<tree*,int>(&station[src],price)); } pair<int,int> res=gen_tree(station,N); printf("%d %d\n",res.first,res.second); }
Тепер Задачі, по яким бал трохи не повний
DDR3
Як і у багатьох, у мене по цій задачі 57 балів. Чи не може журі повідомити той злощасний тест, на якому повалилися всі рішення, крім одного?
Ідея рішення - бінарний пошук, і куча формул, які треба дуже довго пояснювати. Може коли з’явиться натхнення, і якщо комусь потрібно буде пояснення до цих формул - напишу.
#include <iostream> #include <cmath> using namespace std; typedef long long ll; typedef unsigned long long ull; typedef unsigned ul; typedef long double ld; ull fast_pow(ull n,ull power) { ull res=1; while(power) { if(power&1) res*=n; n*=n; power>>=1; } return res; } ull n_digits_count(ull n) { if(n==1)return 9; return 9*fast_pow(10,n-1); } ull str_len(ull n) { ull res=0; ull t=ceil(log10(n+1)); for(ull i=1;i<t;++i) { res+=i*n_digits_count(i); } res+=t*(n-fast_pow(10,t-1)+1); return res; } ull get_ith_digit(ull n,ull i,ull t) { return (n/fast_pow(10,t-i))%10; } ull consecutive_repeats(ull n,ull t,bool init=1) { if(n<10&&t<2)return 0; ull res=0; ull threshold=fast_pow(10,t-1); ull first,second; if(n>=threshold) { first=get_ith_digit(n,1,t); second=get_ith_digit(n,2,t); } else if(n>=threshold/10) { first=0; second=get_ith_digit(n,1,t-1); } else first=second=0; res+=(first-init)*fast_pow(10,t-2)*(t-1); if(second>first)res+=fast_pow(10,t-2); else if(second==first)res+=n%fast_pow(10,t-2)+1; return res+consecutive_repeats(n%fast_pow(10,t-1),t-1,0); } ull repeats_count(ull n) { if(n<10)return 0; ull t=ceil(log10(n+1)); ull first=get_ith_digit(n,1,t),last=get_ith_digit(n,t,t); ull res=(first-1)*fast_pow(10,t-2)+(n/10)%fast_pow(10,t-2)+(last>first)+(first==9); return res+consecutive_repeats(n,t)+repeats_count(fast_pow(10,t-1)-1); } ull solver(ull n) { return str_len(n)-repeats_count(n); } ull bin_search(ull l, ull r, ull query, ull(*f)(ull)) { for(ull i=0;i<200&&l<r;++i) { ull mid=l+((r-l)>>1); if(f(mid)<query)l=mid; else r=mid; } return query==f(l)?l:r; } int main() { ull n; cin>>n; cout<<bin_search(1,fast_pow(10,19),n,solver)<<endl; }
Tourist
Завжди недолюблював задачі на геометрію і отримав тут 54 бали
Моя ідея була такою:
Будуємо для нашого набору точок опуклу оболонку і перевіряємо, щоб точки з початкового набору йшли в порядку обходу опуклої оболонки, при чому не обходили її більше одного разу.
Але, як це часто буває, після якоїсь правки з’явився баг, на перший погляд, абсолютно з тією правкою не пов’язаний. А помітив я його уже після відправки.
Баг я так і не виправив, та й код цієї задачі у мене особливою красою і ясністю не відрізняється, тому я його й викладати не буду
Тим більше, як я теж дізнався після відправки своєї програми - існує значно простіше і красивіше рішення - рахувати, скільки разів змінювався напрям руху по вісях Х та У. В усякому разі, одна з реалізацій(не моя) цієї ідей набирає 56 балів.
Відредаговано Dim_ov (2012-02-03 21:06:29)
Поза форумом
Dim_ov написав:
існує значно простіше і красивіше рішення - рахувати, скільки разів змінювався напрям руху по вісях Х та У. В усякому разі, одна з реалізацій(не моя) цієї ідей набирає 56 балів.
Мова точно йде про використання ТІЛЬКИ цієї ідеї??? У мене виходить, начебто тільки її використання дає 26 балів, що теж наче й забагато, але терпимо.
ГАРАНТУЮ, що відповідь НЕ буде використана для того, щоб зменшити бали за відповідний розв"язок. Результати оголошені (нехай лише як попередні, але оголошені), й зміна тестів гіпотетично можлива ЛИШЕ у разі виявлення явної помилки в тестах. А такого поки що нема (і, сподіваюсь, не буде).
- - -
Беручи статистику по всім учасникам, найбільше вердиктів "(Wrong Answer)" по тесту №16. Я не_передивлявся усі розв"язки, і навряд чи це робитиму, але серед тих, які передивився, абсолютно всі не_враховували ситуацію, коли одночасно A[1]==A[2] та A[n-1]==A[n] і при цьому сАме вершина A[n] єдина порушує опуклість.
Друге місце за кількістю вердиктів "(Wrong Answer)" займає тест №21. Він має вигляд 3 4 0 0 3 3 1 1 4 4 3 0 0 3 3 1 1 2 0 0 3 3 (або, якщо відформатувати у більш красивому вигляді, то
3
4 0 0 3 3 1 1 4 4
3 0 0 3 3 1 1
2 0 0 3 3
) і відповідь 0 1 1.
Значну кількість вердиктів "(Wrong Answer)" (6-те місце) має також тест №21, який має вигляд 2 4 -99999998 -99999999 0 0 99999999 100000000 17 42 4 -99999998 -99999999 0 0 99999999 100000000 42 17 (або, якщо відформатувати у більш красивому вигляді, то
2
4 -99999998 -99999999 0 0 99999999 100000000 17 42
4 -99999998 -99999999 0 0 99999999 100000000 42 17
) і відповідь 0 1. Проблема в тому, що при обчисленні напрямку повороту у вершині з координатами 0 0 значна кількість способів дають помилкову відповідь "сусідні з цією вершиною сторони спінапрямлені" (тоді як фактично відбувається дуже малий поворот у від"ємному напрямку (за годинниковою стрілкою)). Аналогічна ситуація у тестах 13, 14 і 30 (але в кожному з них це не єдина проблема).
- - -
Взагалі -- дуже цікавить, які саме ідеї все-таки вдасться довести до повного бала. Також дуже цікавлять всі думки щодо того, як правильно створювати набір тестів по цій задачі.
Поза форумом
Ilya Porublyov написав:
Мова точно йде про використання ТІЛЬКИ цієї ідеї??? У мене виходить, начебто тільки її використання дає 26 балів, що теж наче й забагато, але терпимо.
Зараз сказати не можу, деталями особливо не цікавився. У понеділок спитаю, чи використовувалось там ще щось і відповім.
Поза форумом
Я не можу зрозуміти чому моя програма dividers проходе тест на 7 балів.Ще до того тест пише:
FAILED (Wrong Answer)
моя програма просто бере і переділяе всі числа на проміжку.
Ось її код:
#include <iostream> using namespace std; int main() { int i,min=1,ost,k=0,kg=1,max,h=1; cin>>max; for(i=min;i<max;i++) {h=1; for(h;h<=i;h++) { ost=i%h; if (ost==0) {k++;} else {ost=0;}} if (kg>k) {k=0;} else {kg=k; k=0;} } cout<<kg; }
В чому проблема???
Поза форумом
byme8 написав:
Я не можу зрозуміти чому моя програма dividers проходе тест на 7 балів.Ще до того тест пише:
FAILED (Wrong Answer)
моя програма просто бере і переділяе всі числа на проміжку.
Ось її код:
...
В чому проблема???
Змініть тип int на unsigned long long і умову зовнішнього циклу виправте.
Замість
i<max
Напишіть
i<=max
І отримаєте правильне, але неймовірно повільне рішення, яке заслужено бере свої 8 балів.
Відредаговано Dim_ov (2012-02-04 09:22:32)
Поза форумом
Ilya Porublyov написав:
Також дуже цікавлять всі думки щодо того, як правильно створювати набір тестів по цій задачі.
Вчора не подивився, на яких тестах валиться моя програма, і чому. А сьогодні з подивом помітив, що валиться вона не на ВА, а на ТЛ-ах у 2, 4 і 5 тестах. Тобто мій баг, схоже, виявився не поміченим тестами(хіба-що, потрібні тести, за збігом, були серед ТЛ-них).
Моя програма не завжди працювала для випадків, коли опукла оболонка обходиться кілька разів. Тобто, наприклад, на тест типу
1 6 0 0 2 2 4 0 0 0 2 2 4 0
Вона видавала одиницю, тоді як правильна відповідь - 0.
Відредаговано Dim_ov (2012-02-04 16:32:09)
Поза форумом
Ні, все-таки тестів, подібних до того, що я виклав вище не було.
Ilya Porublyov написав:
Взагалі -- дуже цікавить, які саме ідеї все-таки вдасться довести до повного бала.
Для цього набору тестів я свою довів до 60.
Ось код.
Сильно за нього не бийте - виправлявся він нашвидкоруч, може бути багато зайвих, чи навіть безглуздих, перевірок, і кількаразовий обхід оболонки досі опрацьовується неправильно.
Поза форумом
Dim_ov в мене до Вас питання. Можете будь ласка дати свій скайп або аську або ще щось якщо є. Дякую...
Поза форумом
Ilya Porublyov написав:
Мова точно йде про використання ТІЛЬКИ цієї ідеї??? У мене виходить, начебто тільки її використання дає 26 балів, що теж наче й забагато, але терпимо.
Ні, ще виконувалася "звичайна" перевірка фігури на опуклість(Всі повороти мають бути в одну сторону)
Поза форумом
Є приклад фігури, у якої всі повороти в одну сторону, але вона не опукла.
Поза форумом
VIRUS написав:
Є приклад фігури, у якої всі повороти в одну сторону, але вона не опукла.
Подивіться діалог спочатку. Крім перевірки на те, що всі повороти в одну сторону, ще рахувалась кількість змін напрямку руху по вісях Х та У
Поза форумом
VIRUS написав:
Є приклад фігури, у якої всі повороти в одну сторону, але вона не опукла.
Це твердження ("дійсно, є") чи запитання ("а що, є?")?
Якщо запитання -- то так, є, наприклад, п"ятикутна зірка, якщо її малювати з 1-ї вершини у 3-тю, далі в 5-ту, далі в 2-ту, далі в 4-ту, далі вернутися в 1-шу.
Dim_ov написав:
не завжди працювала для випадків, коли опукла оболонка обходиться кілька разів.
Dim_ov написав:
Ні, все-таки тестів, подібних до того, що я виклав вище не було.
Дійсно, не було.
Дійсно, в даній задачі передбачити у тестах все виявилося досить складно. Ще_складніше, ніж розглянути всі випадки у програмі--розв"язку, а з цим теж ніхто з учасників не_впорався...
Причому, в 23-му тесті таки є маршрут
16
-303 -569
-399 -462
-444 -401
-378 -47
-303 -569
-399 -462
-444 -401
-378 -47
-206 217
106 429
568 465
677 430
1034 196
1186 -34
1288 -276
1092 -732
і ще в 26-му щось подібне. Я чомусь думав що такий тест має виявити ті самі проблеми розв"язків, що й кількакратний обхід оболонки. Як виявилося -- не обов"язково.
Dim_ov написав:
ще виконувалася "звичайна" перевірка фігури на опуклість(Всі повороти мають бути в одну сторону)
Так обидві перевірки ("всі повороти в одну сторону" та "правильна кількість змін напрямку руху по вісях Х та У") разом узяті, якщо їх правильно дошліфувати в усіх дрібницях, дають повний розв'язок! І 56 балів -- не забагато, а, навпаки, 4 бали втрачені, бо погано враховані якісь дрібниці.
Поза форумом
Ilya Porublyov написав:
Це твердження ("дійсно, є") чи запитання ("а що, є?")?
Там не було знаку запитання!
Поза форумом