На форумі обговорюються лише питання, пов'язані з олімпіадою
Ви не зайшли.
Задача Unizero.
Припустимо, що у нас є поле 00...00 (довжиною n). І за умовою задачі нам потрібно розтавити одиниці (більш ніж одну) так, щоб не суперечило даній умові. Випадки розміщення будуть наступні:
1) Через один нуль (00..01010..000): щоб не порушити умову, будем розтавляти такі послідовності, як: 101, 10101,1010101 і т.д. Щоб знайти кількість позицій для кожної такої послідновності в нашому полі, потрібно до загальної суми додати різницю довжини поля і дожину вибраної послідовності + 1. Для кращого розуміння розглянемо поле довжиною 6 (000000). Кількість позицій довжиною в 3 (101) = 6 - 3 + 1 (позиції: 101000, 010100,001010,000101), довжиною в 5 (10101) = 6-5+1 (101010, 010101). Отже відповідь для послідовностей "через один нуль" буде : (n-2)+(n-4)+(n-6)... .
2) Через к нулів (00..0100..{k}..0010..00). Порядок обчислення буде аналогічний прикладу через один нуль, а саме сума = (n-(k)+1)+(n-(2*k)+1)+(n-(3*k)+1)...
Замітимо, що коли будемо підраховувати загальну суму усіх (n-i+1), даний вираз (n-i+1), де 1<i<n, буде зустрічатися рівно числу кільності дільників числа i.
Отже, для знаходження відповіді на поставлену задачу, потрібно підрахувати суму( (n-i)*(кількість_дільників(i)) ).
Код вийшов наступний:
for(int i=2;i<n;i++)
for(int j=i;j<n;j+=i)
a[j]++;
ll sm = 0;
for(int i=2;i<n;i++)
sm+=a[i]*(n-i);
cout<<sm;
Напевно це рішення не є подібним авторському, але ,не знаю чому, дане рішення бере нуль балів (Time limit), хоча відправка після завершення 2 туру показала, що мінімальний час виконання:
(0.06 c - 0.07 c) - і це 19 тестів, 20 тест - 0,1 с, і остальні по 0,52 с.
Чесно кажучи, не розумію таку систему оцінювання ![]()
Відредаговано PAS99 (2015-12-22 10:47:40)
Поза форумом
Задача Unizero.
Ніби все добре але проходить 26 з 40. Допоможіть, не знаю в чому пробема.
ПС. Тормозить на більших тестах
var k,l:int64;
procedure func(k,i,r:int64; var l:int64);
var b,a,n:int64;
begin
b:=k-2*i;
a:= b mod i;
n:=1+b div i;
if(b>0) then func(k, i+1, r+(a+b)*n div 2,l) else l:=r;
end;
begin
readln(k);
func(k,1,0,l);
writeln(l);
end.
Поза форумом
PAS99 написав:
Задача Unizero.
Припустимо, що у нас є поле 00...00 (довжиною n). І за умовою задачі нам потрібно розтавити одиниці (більш ніж одну) так, щоб не суперечило даній умові. Випадки розміщення будуть наступні:
1) Через один нуль (00..01010..000): щоб не порушити умову, будем розтавляти такі послідовності, як: 101, 10101,1010101 і т.д. Щоб знайти кількість позицій для кожної такої послідновності в нашому полі, потрібно до загальної суми додати різницю довжини поля і дожину вибраної послідовності + 1. Для кращого розуміння розглянемо поле довжиною 6 (000000). Кількість позицій довжиною в 3 (101) = 6 - 3 + 1 (позиції: 101000, 010100,001010,000101), довжиною в 5 (10101) = 6-5+1 (101010, 010101). Отже відповідь для послідовностей "через один нуль" буде : (n-2)+(n-4)+(n-6)... .
2) Через к нулів (00..0100..{k}..0010..00). Порядок обчислення буде аналогічний прикладу через один нуль, а саме сума = (n-(k)+1)+(n-(2*k)+1)+(n-(3*k)+1)...
Замітимо, що коли будемо підраховувати загальну суму усіх (n-i+1), даний вираз (n-i+1), де 1<i<n, буде зустрічатися рівно числу кільності дільників числа i.
Отже, для знаходження відповіді на поставлену задачу, потрібно підрахувати суму( (n-i)*(кількість_дільників(i)) ).
Код вийшов наступний:
for(int i=2;i<n;i++)
for(int j=i;j<n;j+=i)
a[j]++;
ll sm = 0;
for(int i=2;i<n;i++)
sm+=a[i]*(n-i);
cout<<sm;
Напевно це рішення не є подібним авторському, але ,не знаю чому, дане рішення бере нуль балів (Time limit), хоча відправка після завершення 2 туру показала, що мінімальний час виконання:
(0.06 c - 0.07 c) - і це 19 тестів, 20 тест - 0,1 с, і остальні по 0,52 с.
Чесно кажучи, не розумію таку систему оцінювання
Из вашего разбора уже видно, что нужно посчитать сумму арифметических прогрессий с шагом - 2, 3 и т.д. Поскольку сумма АП считается за О(1). То такое решение не превосходило бы О(n).
Поза форумом
Я розумію, що рішення не ідеальне, але проходячи 80% тестів не більше, ніж за 0,1 с , воно б мало набирати "хоч щось" ![]()
Поза форумом
На всякий случай приведу полнобальные решения (они отличаются от того, что я посылал на тур):
Unizero
#include <iostream>
int main()
{
long long n,c,i,ans=0;
std::cin>>n;
for(i=2;i<n;++i)
{
c=(n+i-1)/i;
ans+=n*(c-1)-i*c*(c-1)/2;
}
std::cout<<ans;
return 0;
}Идея: формула решения , внутренняя сумма раскрыта для ускорения вычислений. Желающие могут попробовать раскрыть и внешнюю сумму.
Сложность: O(n) времени, O(1) памяти.
Walk
#include <cstdio>
long long egcd(long long a,long long b,long long &x,long long &y)
{
if(b==0)
{
x=a<0?-1:+1;
y=0;
return a<0?-a:a;
}
long long c=a/b;
long long p,q;
long long ret=egcd(b,a%b,p,q);
x=q;
y=p-c*q;
return ret;
}
long long inv_m(long long a,long long b)
{
long long x,y;
egcd(a,b,x,y);
return ((x%b)+b)%b;
}
long long a(long long n)
{
const long long m=1000000007LL;
long long ret=0;
long long c=1;
for(long long i=0;i<n+1;++i)
{
ret=(ret+c)%m;
c=(((c*2*(2*i+1))%m)*inv_m(i+2,m))%m;
}
return ret;
}
int main()
{
long t;
scanf("%ld",&t);
printf("%ld",(long)a(((long long)t-1LL)/2LL));
return 0;
}Идея: беглый взгляд на OEIS находит выражение для ответа через сумму чисел Каталана. Для чисел Каталана есть рекуррентная формула, поэтому (частичная) сумма и следующее число Каталана считаются вместе. Формула содержит деление, но т. к. 1000000007 - простое число, то мы имеем право "делить по модулю", т. е. умножать на обратное по модулю 1000000007.
Сложность: O(t) времени, O(1) памяти.
Hanoy2015
#include <iostream>
#include <algorithm>
int main()
{
int N;
int a[64];
int c=0;
unsigned long long answer=0;
std::cin>>N;
for(int i=0;i<N;++i)
std::cin>>a[i];
std::sort(a,a+N);
for(int i=N-1;i>=0;--i)
answer+=1LL<<(c+=(i<N-1&&a[i]!=a[i+1]));
std::cout<<answer;
return 0;
}Идея: сортируем диски, дальше - обычные Ханойские башни, но стопка из k одинаковых дисков воспринимается как 1 диск со стоимостью перемещения в k ходов.
Сложность: O(N) времени, O(1) памяти.
Schoolnet2015n
#include <cstdio>
static const int MAX_N=1000;
bool n[MAX_N][MAX_N]={{0}};
int c[MAX_N]={0};
int s[MAX_N]={0};
int N;
int dfs(int i)
{
int ret=1;
if(c[i]) return 0;
c[i]=1;
for(int j=0;j<N;++j)
if(n[i][j])
ret+=dfs(j);
return ret;
}
int main()
{
int a,b=0,c;
scanf("%d",&N);
for(int i=0;i<N;++i)
for(int j=0;j<N;++j)
{
scanf("%d",&a);
n[i][j]=a;
}
for(int i=0;i<N;++i)
{
if((a=dfs(i))>b)
{
b=a;
c=i+1;
}
}
printf("%d %d",b,c);
return 0;
}Идея: стандартная задача.
Сложность: O(N^2) времени, O(N^2) памяти.
FactZero
#include <cstdio>
int main()
{
long n,k;
long m=0;
long p[32]={};
long c[32]={};
long b=2000000000L;
scanf("%ld%ld",&n,&k);
for(long d=2,t=k;t>1;++d)
{
if(t%d==0)
{
while(t%d==0)
{
++c[m];
t/=d;
}
p[m++]=d;
}
}
for(int i=0;i<m;++i)
{
long q=n;
long t=0;
while(q>0) {q/=p[i];t+=q;}
t/=c[i];
if(t<b) b=t;
}
printf("%ld\n",b);
return 0;
}Идея: раскладываем k на простые множители, смотрим, с какой кратностью каждый множитель входит в n!. Ищем минимум (с учётом кратностей в k).
Сложность: O(k+log(n)*log(k)) времени, O(log(k)) памяти.
Правка: временная сложность в FactZero.
Відредаговано FordPerfect (2016-01-12 17:50:45)
Поза форумом
FordPerfect, дякую за надані рішення. Вважаю дуже корисним для зацікавлених учасників ознайомлення з рішеннями саме після закінчення туру, коли умови задач іще "крутяться" в голові.
Поза форумом
Hanoy2015
Важко конкурувати з лаконічністю рядка
answer+=1LL<<(c+=(i<N-1&&a[i]!=a[i+1]));
в рішенні FordPerfect, але насмілюсь викласти і своє повнобальне рішення.
#include <iostream>
#include <cmath>
using namespace std;
int main()
{
int N, a, n[10001]={};
cin >> N;
for (int i=1; i<=N; i++)
{
cin>>a;
n[a]++;
}
long double res=0;
for (int i=10000,k=0; i>0; i--)
{
if (n[i]!=0)
{
res+=pow(2.0,k)*n[i];
k++;
}
}
cout << (unsigned long long)res;
return 0;
}Алгоритм такий:
В класичному варіанті кількість перекладань дорівнює: 2^n -1
Якщо перекладати диски пачками, то так воно і буде (n – кількість «пачок»).
Але кожна пачка перекладається поштучно, тобто найбільші диски перекладаються один раз (2^0*а), де а-кількість найбільших дисків, далі 2^1*в, де в – кількість менших дисків, далі 2^2*с, де с – кількість ще менших дисків і т.д.
1) Підраховуємо яких і скільки дисків є за допомогою додаткового масиву n[10000] під час уведення.
2) Потім знайдемо суму перекладань.
Обрахунки виконуємо в дійсних числх, а результат виводимо в максимально можливому типі цілих.
Відредаговано LVV (2015-12-24 06:31:12)
Поза форумом
Кстати в строчке
answer+=1LL<<(c+=(i<N-1&&a[i]!=a[i+1]));
всё-таки нет undefined behavior, а только implementation-defined. Но, всё-равно, лучше, наверное:
answer+=1ULL<<(c+=(i<N-1&&a[i]!=a[i+1]));
Поза форумом
код розвязку задачі Unizero на основі знаходження сум арифметичних прогресій:
var l,f:int64;
n,i,j,k,nach:longint;
begin
read(n);
n:=n-2;l:=0;k:=1;
while (n>0)do begin
if k=1 then begin
f:=n;
nach:=1;
end else begin
nach:=n mod k;
if nach=0 then nach:=k;
f:=(n-nach)div k+1;
end;
l:=l+(nach+n)*f div 2;
n:=n-2;k:=k+1;
end;
writeln(l);
end.протокол тестування:
Тест Результат Час роботи
00 PASSED (+0) 0.01 с
01 PASSED (+1) 0.01 с
02 PASSED (+1) 0.01 с
03 PASSED (+1) 0.01 с
04 PASSED (+1) 0.01 с
05 PASSED (+1) 0.01 с
06 PASSED (+1) 0.01 с
07 PASSED (+1) 0.01 с
08 PASSED (+1) 0.01 с
09 PASSED (+1) 0.01 с
10 PASSED (+1) 0.01 с
11 PASSED (+2) 0.01 с
12 PASSED (+2) 0.01 с
13 PASSED (+2) 0.01 с
14 PASSED (+2) 0.01 с
15 PASSED (+2) 0.01 с
16 PASSED (+2) 0.01 с
17 PASSED (+2) 0.01 с
18 PASSED (+2) 0.01 с
19 PASSED (+2) 0.02 с
20 PASSED (+2) 0.04 с
21 PASSED (+2) 0.04 с
22 PASSED (+2) 0.09 с
23 PASSED (+2) 0.10 с
24 PASSED (+2) 0.14 с
25 PASSED (+2) 0.17 с
Поза форумом
Unizero, праздник продолжается:
#include <iostream>
// sum[i=1;n] floor(n/i)
long long s1(long long n)
{
long long ret=0;
long long i=1;
for(i=1;i*i<=n;++i)
ret+=2*(n/i);
--i;
ret-=i*i;
return ret;
}
// sum[i=1;n] floor(n/i)*i
long long s2( long long n)
{
long long ret=0;
long long i=1;
for(i=1;i*i<=n;++i)
{
long long a=n/(i+1);
long long b=n/i;
ret+=b*i;
if(i<b) ret+=i*(b*(b+1)-a*(a+1))/2;
}
return ret;
}
// sum[i=1;n] floor(n/i)^2*i
long long s3(long long n)
{
long long ret=0;
long long i=1;
for(i=1;i*i<=n;++i)
{
long long a=n/(i+1);
long long b=n/i;
ret+=b*b*i;
if(i<b) ret+=i*i*(b*(b+1)-a*(a+1))/2;
}
return ret;
}
int main()
{
long long n;
std::cin>>n;
std::cout<<n*s1(n-1)-(s3(n-1)+s2(n-1))/2-n*(n-1)/2;
return 0;
}Сложность: O(sqrt(n)) времени, O(1) памяти.
Идея: можно воспользоваться формулой из http://math.stackexchange.com/questions … rogression и обобщить её следующим образом:
(правка: исправил формулу)
Версии для ceil легко получить, используя тот факт, что ceil(n/i)=floor((n+i-1)/i)=floor((n-1)/i)+1.
Таким образом, формула из поста #6 может быть выражена через приведённые выше суммы.
Відредаговано FordPerfect (2016-01-17 06:47:10)
Поза форумом