T1
Описание
Известные \ (А G_0 = \) , \ (G_1 = В \) , и имеет
\ [G_n = 3g_ {п-1} {-g_ п-2} (п> 1) \]
Известные \ (п-Р- {} = 0 п \) , и
\ [F_ {п, к} = F_ {G_n, к-1} (к> 0) \]
Принимая во внимание (а, б, п, к , р \) \ значение, то получим \ (f_ {п, к} \ BMOD р \) результаты.
\ (0 ≤ а, б <р \) , \ (1 ≤ T ≤ 1000 \) , \ (1 ≤ п, р ≤ 10 ^ 9 \) , \ (1 ≤ K ≤ 100 \) .
Решение
Сначала рассмотрим , как бек \ (G_N g_ {} \) .
\ (G_n \) может быть большим, рассмотреть возможность обращения \ (G_n \) раздел цикла.
Примечание \ (F_n \) есть число Фибоначчи \ (п- \) ключа, таблица игра может быть найдена в \ (G_N = Р- {2N-Р- {B} × 2 (N- , . 1)} × А \) .
Таким образом , \ (д \) участок петли с тем же делом Фибоначчи последовательности чисел.
В частности, обратите внимание на режиме столбца Фибоначчи \ (р \) минимальная циркуляция в значении раздела как \ (\ PI (р-) \) .
Если \ (. Р = \ 1 prod_ {Я} = ^ {^ wp_i a_i} \) (разложение добротности), то:
\ [\ Р (р) = \ {Текст LCM} _ {I = 1} ^ ш (ч (p_i) p_i ^ {a_i-1}) \]
среди
\ [Н (р) = \ начинаются {случаи} 3 & р = 2 \\ 20 & р = 5 \\ р-1 & р = ± 1 \ pmod5 \\ 2р + 2 & р = ± 2 \ pmod5 \\ \ конец {случаи } \]
Доказанный тоже (I) комплексы (также) Разный (нет) (будет), то не запустятся.
Временная сложность \ (O (жить) \) ,
Код
#include <bits/stdc++.h>
using namespace std;
#define ll long long
template <class t>
inline void read(t & res)
{
char ch;
while (ch = getchar(), !isdigit(ch));
res = ch ^ 48;
while (ch = getchar(), isdigit(ch))
res = res * 10 + (ch ^ 48);
}
template <class t>
inline void print(t x)
{
if (x > 9) print(x / 10);
putchar(x % 10 + 48);
}
const int e = 1e7 + 5;
struct matrix
{
int r, c;
ll a[2][2];
};
ll now_p, A, B;
int L = 1e7, cnt, pri[e];
bool bo[e];
inline void init()
{
int i, j;
for (i = 2; i <= L; i++)
{
if (!bo[i]) pri[++cnt] = i;
for (j = 1; j <= cnt && i * pri[j] <= cnt; j++)
{
bo[i * pri[j]] = 1;
if (i % pri[j] == 0) break;
}
}
}
inline ll mul(ll x, ll y)
{
ll res = x * y - (ll)((long double)x * y / now_p + 1e-8) * now_p;
return res < 0 ? res + now_p : res;
}
inline void add(ll &x, ll y)
{
(x += y) >= now_p ? x -= now_p : 0;
}
inline matrix operator * (matrix a, matrix b)
{
matrix c;
memset(c.a, 0, sizeof(c.a));
int i, j, k;
for (i = 0; i < a.r; i++)
for (k = 0; k < b.r; k++)
for (j = 0; j < b.c; j++)
add(c.a[i][j], mul(a.a[i][k], b.a[k][j]));
c.r = a.r; c.c = b.c;
return c;
}
inline ll calc(ll n)
{
matrix res, c;
res.a[1][0] = res.a[1][1] = 0;
res.a[0][0] = A % now_p; res.a[0][1] = B % now_p;
res.r = 1; res.c = 2;
c.a[0][0] = 0; c.a[0][1] = now_p - 1;
c.a[1][0] = 1; c.a[1][1] = 3;
c.r = c.c = 2;
while (n)
{
if (n & 1) res = res * c;
n >>= 1;
c = c * c;
}
return res.a[0][0];
}
inline ll get_g(ll p)
{
return p == 2 ? 3 : p == 5 ? 20 : p % 5 == 1 ? p - 1 : p % 5 == 4 ? p - 1 : 2 * p + 2;
}
inline ll lcm(ll a, ll b)
{
return a / __gcd(a, b) * b;
}
inline ll find(ll x)
{
ll res = 1;
int i;
ll s = sqrt(x);
for (i = 1; i <= cnt && pri[i] <= s; i++)
if (x % pri[i] == 0)
{
int p = pri[i];
ll t = get_g(p);
while (x % p == 0)
{
x /= p; t *= p;
}
t /= p;
res = lcm(res, t);
}
if (x != 1) res = lcm(res, get_g(x));
return res;
}
inline ll solve(ll n, ll k, ll p)
{
if (!k) return n % p;
ll cir = find(p), nxt = solve(n, k - 1, cir);
now_p = p;
ll res = calc(nxt);
return res;
}
inline void work()
{
read(A); read(B);
int n, k, p;
read(n); read(k); read(p);
ll ans = solve(n, k, p);
print(ans); putchar('\n');
}
int main()
{
freopen("hakugai.in", "r", stdin);
freopen("hakugai.out", "w", stdout);
init();
int T;
read(T);
while (T--) work();
fclose(stdin);
fclose(stdout);
return 0;
}
T2
Описание
Учитывая , а \ (п \) точек дерева, точки пронумерованы \ (0 \ -n-SIM. 1 \) .
Вы можете \ (к \) операций, каждую операцию вы можете удалить текущее дерево любой одной стороны, а затем добавить ребро. Вы должны убедиться , что после каждой операции текущей карты остается дерево, и каждый удаление после края могут быть добавлены и удалены один и тот же боковой край.
Теперь вы считаете, в \ (к \) после операции, в общей сложности , сколько деревьев может образоваться будет.
Различные формы двух деревьев, если и только если существует сторона \ ((X, Y) \) , которая появляется в дереве, дерево не появляется в другом.
\ (1 \ п Leq \ Leq 50 \) , \ (0 \ Leq к <п \) .
Решение
Название есть вопрос: Сколько дерев встречаются \ (K \) кромки не оригинальное дерева.
Определяет край \ ((и, v) \ ) веса: Если \ ((и, v) \ ) в исходном дереве, масса \ (1 \) , в противном случае \ (X \) .
Мы требуем , чтобы правая сторона есть произведение \ (\ ле х ^ к \ ) числа остовных деревьев.
Хорошо известно, что определение продукта правой сторона остовного дерева массы, значение матрицы может быть определен вес дерева теоремы и все связующих дерева.
В этом вопросе, сумма весов полученных содержащий \ (Х \) а \ (N-1 \) полином порядка, \ (Х ^ I \) коэффициент является вес \ (I \) из количество связующего дерева, \ (Х- ^ 0 \ SIM - Х- ^ к \) коэффициент сумма ответ.
Я обнаружил , не так легко найти многочлен непосредственно, рассмотрим \ (\ текст {Лагранж} \ ) интерполяции, то есть так \ (X =. 1 \ п-SIM \) , значение , полученное из теоремы полиномиальная матрица дерева.
Полученные значения всех заместителей \ (\ текст {Лагранж} \ ) интерполяционной формулы можно восстановить многочлен.
Временная сложность \ (O (п ^. 4) \) .
Код
#include <bits/stdc++.h>
using namespace std;
#define ll long long
template <class t>
inline void read(t & res)
{
char ch;
while (ch = getchar(), !isdigit(ch));
res = ch ^ 48;
while (ch = getchar(), isdigit(ch))
res = res * 10 + (ch ^ 48);
}
const int e = 155, mod = 998244353;
bool bo[e][e];
int a[e][e], inv[e], ans, n, m, c[e], d[e], h[e];
inline void add(int &x, int y)
{
(x += y) >= mod && (x -= mod);
}
inline void del(int &x, int y)
{
(x -= y) < 0 && (x += mod);
}
inline int mul(int x, int y)
{
return (ll)x * y % mod;
}
inline int ksm(int x, int y)
{
int res = 1;
while (y)
{
if (y & 1) res = mul(res, x);
y >>= 1;
x = mul(x, x);
}
return res;
}
inline int gauss(int n)
{
int i, j, k, res = 1;
for (i = 1; i < n; i++)
{
int x = 0;
for (j = i; j <= n; j++)
if (a[j][i])
{
x = j;
break;
}
if (!x) continue;
if (x != i) res = mod - res, swap(a[x], a[i]);
int inv = ksm(a[i][i], mod - 2);
for (j = i + 1; j <= n; j++)
{
int v = mul(a[j][i], inv);
for (k = i; k <= n; k++) del(a[j][k], mul(a[i][k], v));
}
}
for (i = 1; i <= n; i++) res = mul(res, a[i][i]);
return res;
}
inline int calc(int bas)
{
int i, j;
for (i = 1; i <= n; i++)
for (j = 1; j <= n; j++)
a[i][j] = 0;
for (i = 1; i < n; i++)
for (j = i + 1; j <= n; j++)
{
int v = 1;
if (!bo[i][j]) v = bas;
add(a[i][i], v); add(a[j][j], v);
del(a[i][j], v); del(a[j][i], v);
}
return gauss(n - 1);
}
int main()
{
freopen("kaisou.in", "r", stdin);
freopen("kaisou.out", "w", stdout);
read(n); read(m);
int i, x, j, k;
inv[1] = 1;
for (i = 2; i <= n; i++)
{
read(x);
x++;
bo[x][i] = bo[i][x] = 1;
inv[i] = mul(mod - mod / i, inv[mod % i]);
}
for (i = 1; i <= n; i++)
{
int y = calc(i);
for (j = 1; j <= n; j++) c[j] = 0;
c[0] = 1;
for (j = 1; j <= n; j++)
if (j != i)
{
if (i < j) y = mul(y, mod - inv[j - i]);
else y = mul(y, inv[i - j]);
for (k = 0; k <= n; k++)
{
h[k] = mul(mod - j, c[k]);
if (k) add(h[k], c[k - 1]);
}
for (k = 0; k <= n; k++) c[k] = h[k];
}
for (j = 0; j <= n; j++) add(d[j], mul(c[j], y));
}
for (i = 0; i <= m; i++) add(ans, d[i]);
cout << ans << endl;
fclose(stdin);
fclose(stdout);
return 0;
}
T3
Описание
Дерево дана взвешенное стороне, удовлетворено вы получите \ (U \) и \ (V \) краевые веса всех ребер в кратчайшем простом пути \ (\ mathrm {} \ GCD ) из \ ( 1 \) указывает на \ ((U, V) ( и <о) \) числа.
Мало того, что вам приходится иметь дело с \ (Q \) действует поправка с правой стороны после каждого изменения, Вы должны ответить на выход текущей ситуации.
\ (2 ≤ N ≤ 10 ^ 5 \) , \ (Q ≤ 100 \) , \ (1 ≤ W, X ≤ 10 ^ 6 \) .
Решение
Во- первых, \ (АНС = \ sum_ {I =. 1} ^ {10 ^. 6} \ MU (I) , F (I) \) , где \ (е (я) \) представляет \ (\ mathrm {НОД} \ BMOD я = 0 \) является числом путей.
Впоследствии, каждое ребро \ ((U, V, W ) \) делится на \ ((U, V, W_1), (U, V, w_2), \ ТОЧКИ, (U, V, W_k) \) .
В котором \ (w_1 \ сим w_k \) является \ (W \) все удовлетворяют \ (\ му \ пе 0 \ ) делители.
Так что теперь \ (е (я) \) является право всех сторон \ (я \) числа путей.
Для каждого ребра было изменено \ (Х \) , соответственно , считать время \ (0 \ сим Q \) в, \ (Х \) весы.
Именно этим \ (Q + 1 \) вес время о всех расщепляются на ряд, и маркера времени на каждом краю.
Не был модифицирован для стороны \ (X \) , время на снос ребер \ (- 1 \) , там всегда выражается.
Далее, правая сторона перечисления \ (v \) , вычислить правильное значение \ (v \) стороне этого \ (Q + 1 \) вклад ответов.
В частности, поддерживать постоянный непересекающийся-набор, при сохранении переменной \ (СУММ \) , сказала , что текущая форма , сколько стороны правы \ (v \) путь.
Перечисление времени \ (Т = -1 \ SIM - Q \) .
Во- первых \ (т = -1 \) добавляют непересекающийся-набор, а затем перечисление \ (\ lceil \) вес края отсутствует \ (V \) край \ (\ rfloor \) Время \ (J \ ) , \ (ВНС [J] = СУММЫ + x \ MU (V) , \) .
\ (Т \ GE 0 \) , когда время для каждого \ (T \) добавляют непересекающихся-набор, вычисление \ (ВНС [T] \) после того, как вкладов от всех этих сторон , а затем концентрировали расследование удален.
Ожидаемое время сложности \ (О ((п-Q +) S \ н- войти) \) , где \ (S \) из \ (10 ^ 6 \) числа в пределах среднего числа делителя.
Код
#include <bits/stdc++.h>
using namespace std;
#define ll long long
template <class t>
inline void read(t & res)
{
char ch;
while (ch = getchar(), !isdigit(ch));
res = ch ^ 48;
while (ch = getchar(), isdigit(ch))
res = res * 10 + (ch ^ 48);
}
template <class t>
inline void print(t x)
{
if (x > 9) print(x / 10);
putchar(x % 10 + 48);
}
const int e = 2e5 + 5, o = 1e6 + 5;
struct point
{
int id, t;
point(){}
point(int _id, int _t) :
id(_id), t(_t) {}
};
struct line
{
int x, y, z;
}b[e], c[e];
bool bo[o];
int fa[e], sze[e], n, q, miu[o], L = 1e6, tim[e], vis[e];
ll ans[e], sum;
vector<int>stk, a[o];
vector<point>g[o], h[e];
inline void init()
{
int i, j;
for (i = 1; i <= L; i++) miu[i] = 1;
for (i = 2; i <= L; i++)
if (!bo[i])
{
miu[i] = -1;
a[i].push_back(i);
for (j = i << 1; j <= L; j += i)
{
bo[j] = 1;
if (j / i % i == 0) miu[j] = 0;
else miu[j] = -miu[j];
a[j].push_back(i);
}
}
}
inline int find(int x)
{
while (fa[x]) x = fa[x];
return x;
}
inline void merge(int x, int y)
{
int fx = find(x), fy = find(y);
if (sze[fx] < sze[fy]) swap(fx, fy);
fa[fy] = fx;
sum += (ll)sze[fx] * sze[fy];
sze[fx] += sze[fy];
stk.emplace_back(fy);
}
inline void cut()
{
int fy = stk.back(), fx = fa[fy];
sze[fx] -= sze[fy];
sum -= (ll)sze[fx] * sze[fy];
fa[fy] = 0;
stk.pop_back();
}
inline void ins(int x, int id, int t)
{
point u = point(id, t);
int s, len = a[x].size(), i, all = 1 << len;
for (s = 0; s < all; s++)
{
int y = 1;
for (i = 0; i < len; i++)
if (s & (1 << i)) y *= a[x][i];
g[y].emplace_back(u);
}
}
inline void solve(int x)
{
int i, j, len = g[x].size(), k;
sum = 0;
ll tot = 0;
for (i = 0; i < len; i = j + 1)
{
j = i - 1;
int now_t = g[x][i].t;
while (j < len - 1 && g[x][j + 1].t == now_t)
{
j++;
int pos = g[x][j].id;
merge(b[pos].x, b[pos].y);
}
ll tmp = miu[x] * sum;
if (now_t != -1)
{
ans[now_t] += tmp;
for (k = i; k <= j; k++) cut();
}
else
{
for (k = j + 1; k < len; k++) vis[g[x][k].t] = x;
for (k = 0; k <= q; k++)
if (vis[k] != x) ans[k] += tmp;
}
}
while (stk.size()) cut();
}
int main()
{
freopen("atoranta.in", "r", stdin);
freopen("atoranta.out", "w", stdout);
init();
read(n);
int i, j;
for (i = 1; i < n; i++) read(b[i].x), read(b[i].y), read(b[i].z);
read(q);
for (i = 1; i <= q; i++) read(c[i].x), read(c[i].y), tim[c[i].x] = i;
for (i = 1; i < n; i++)
if (!tim[i]) ins(b[i].z, i, -1);
for (i = 1; i <= q; i++)
{
point u = point(c[i].x, c[i].y);
for (j = i; j <= q; j++)
if (j == i || c[j].x != c[i].x) h[j].emplace_back(u);
else break;
bool pd = 0;
for (j = 1; j < i; j++)
if (c[j].x == c[i].x)
{
pd = 1;
break;
}
if (!pd)
{
point v = point(c[i].x, b[c[i].x].z);
for (j = 0; j < i; j++) h[j].emplace_back(v);
}
}
for (i = 0; i <= q; i++)
{
int len = h[i].size();
for (j = 0; j < len; j++) ins(h[i][j].t, h[i][j].id, i);
}
for (i = 1; i <= n; i++) sze[i] = 1;
for (i = 1; i <= L; i++)
if (g[i].size()) solve(i);
for (i = 0; i <= q; i++) print(ans[i]), putchar('\n');
fclose(stdin);
fclose(stdout);
return 0;
}