2019 牛客 多校赛 第九场
slove 1/10
rank 419
补题 4/10
--------------------------------------------------------
Link
A、The power of Fibonacci
给两个整数 n,m,求斐波那契的前N项的M次方的和
ex杜教BM,存板子
#include <bits/stdc++.h>
using namespace std;
#ifndef ONLINE_JUDGE
#define debug(fmt, ...) fprintf(stderr, "[%s] " fmt "\n", __func__, ##__VA_ARGS__)
#else
#define debug(...)
#endif
// given first m items init[0..m-1] and coefficents trans[0..m-1] or
// given first 2 *m items init[0..2m-1], it will compute trans[0..m-1]
// for you. trans[0..m] should be given as that
// init[m] = sum_{i=0}^{m-1} init[i] * trans[i]
struct LinearRecurrence
{
using int64 = long long;
using vec = std::vector<int64>;
static void extand(vec& a, size_t d, int64 value = 0)
{
if (d <= a.size()) return;
a.resize(d, value);
}
static vec BerlekampMassey(const vec& s, int64 mod)
{
std::function<int64(int64)> inverse = [&](int64 a) {
return a == 1 ? 1 : (int64)(mod - mod / a) * inverse(mod % a) % mod;
};
vec A = { 1 }, B = { 1 };
int64 b = s[0];
for (size_t i = 1, m = 1; i < s.size(); ++i, m++)
{
int64 d = 0;
for (size_t j = 0; j < A.size(); ++j)
{
d += A[j] * s[i - j] % mod;
}
if (!(d %= mod)) continue;
if (2 * (A.size() - 1) <= i)
{
auto temp = A;
extand(A, B.si***t64 coef = d * inverse(b) % mod;
for (size_t j = 0; j < B.size(); ++j)
{
A[j + m] -= coef * B[j] % mod;
if (A[j + m] < 0) A[j + m] += mod;
}
B = temp, b = d, m = 0;
}
else
{
extand(A, B.si***t64 coef = d * inverse(b) % mod;
for (size_t j = 0; j < B.size(); ++j)
{
A[j + m] -= coef * B[j] % mod;
if (A[j + m] < 0) A[j + m] += mod;
}
}
}
return A;
}
static void exgcd(int64 a, int64 b, int64& g, int64& x, int64& y)
{
if (!b)
x = 1, y = 0, g = a;
else
{
exgcd(b, a % b, g, y, x);
y -= x * (a / b);
}
}
static int64 crt(const vec& c, const vec& m)
{
int n = c.size();
int64 M = 1, ans = 0;
for (int i = 0; i < n; ++i) M *= m[i];
for (int i = 0; i < n; ++i)
{
int64 x, y, g, tm = M / m[i];
exgcd(tm, m[i], g, x, y);
ans = (ans + tm * x * c[i] % M) % M;
}
return (ans + M) % M;
}
static vec ReedsSloane(const vec& s, int64 mod)
{
auto inverse = [](int64 a, int64 m) {
int64 d, x, y;
exgcd(a, m, d, x, y);
return d == 1 ? (x % m + m) % m : -1;
};
auto L = [](const vec& a, const vec& b) {
int da = (a.size() > 1 || (a.size() == 1 && a[0])) ? a.size() - 1 : -1000;
int db = (b.size() > 1 || (b.size() == 1 && b[0])) ? b.size() - 1 : -1000;
return std::max(da, db + 1);
};
auto prime_power = [&](const vec& s, int64 mod, int64 p, int64 e) {
// linear feedback shift register mod p^e, p is prime
std::vector<vec> a(e), b(e), an(e), bn(e), ao(e), bo(e);
vec t(e), u(e), r(e), to(e, 1), uo(e), pw(e + 1);
;
pw[0] = 1;
for (int i = pw[0] = 1; i <= e; ++i) pw[i] = pw[i - 1] * p;
for (int64 i = 0; i < e; ++i)
{
a[i] = { pw[i] }, an[i] = { pw[i] };
b[i] = { 0 }, bn[i] = { s[0] * pw[i] % mod };
t[i] = s[0] * pw[i] % mod;
if (t[i] == 0)
{
t[i] = 1, u[i] = e;
}
else
{
for (u[i] = 0; t[i] % p == 0; t[i] /= p, ++u[i])
;
}
}
for (size_t k = 1; k < s.size(); ++k)
{
for (int g = 0; g < e; ++g)
{
if (L(an[g], bn[g]) > L(a[g], b[g]))
{
ao[g] = a[e - 1 - u[g]];
bo[g] = b[e - 1 - u[g]];
to[g] = t[e - 1 - u[g]];
uo[g] = u[e - 1 - u[g]];
r[g] = k - 1;
}
}
a = an, b = bn;
for (int o = 0; o < e; ++o)
{
int64 d = 0;
for (size_t i = 0; i < a[o].size() && i <= k; ++i)
{
d = (d + a[o][i] * s[k - i]) % mod;
}
if (d == 0)
{
t[o] = 1, u[o] = e;
}
else
{
for (u[o] = 0, t[o] = d; t[o] % p == 0; t[o] /= p, ++u[o])
;
int g = e - 1 - u[o];
if (L(a[g], b[g]) == 0)
{
extand(bn[o], k + 1);
bn[o][k] = (bn[o][k] + d) % mod;
}
else
{
int64 coef = t[o] * inverse(to[g], mod) % mod * pw[u[o] - uo[g]] % mod;
int m = k - r[g];
extand(an[o], ao[g].size() + m);
extand(bn[o], bo[g].size() + m);
for (size_t i = 0; i < ao[g].size(); ++i)
{
an[o][i + m] -= coef * ao[g][i] % mod;
if (an[o][i + m] < 0) an[o][i + m] += mod;
}
while (an[o].size() && an[o].back() == 0) an[o].pop_back();
for (size_t i = 0; i < bo[g].size(); ++i)
{
bn[o][i + m] -= coef * bo[g][i] % mod;
if (bn[o][i + m] < 0) bn[o][i + m] -= mod;
}
while (bn[o].size() && bn[o].back() == 0) bn[o].pop_back();
}
}
}
}
return std::make_pair(an[0], bn[0]);
};
std::vector<std::tuple<int64, int64, int>> fac;
for (int64 i = 2; i * i <= mod; ++i)
{
if (mod % i == 0)
{
int64 cnt = 0, pw = 1;
while (mod % i == 0) mod /= i, ++cnt, pw *= i;
fac.emplace_back(pw, i, cnt);
}
}
if (mod > 1) fac.emplace_back(mod, mod, 1);
std::vector<vec> as;
size_t n = 0;
for (auto&& x : fac)
{
int64 mod, p, e;
vec a, b;
std::tie(mod, p, e) = x;
auto ss = s;
for (auto&& x : ss) x %= mod;
std::tie(a, b) = prime_power(ss, mod, p, e);
as.emplace_back(a);
n = std::max(n, a.size());
}
vec a(n), c(as.size()), m(as.size());
for (size_t i = 0; i < n; ++i)
{
for (size_t j = 0; j < as.size(); ++j)
{
m[j] = std::get<0>(fac[j]);
c[j] = i < as[j].size() ? as[j][i] : 0;
}
a[i] = crt(c, m);
}
return a;
}
LinearRecurrence(const vec& s, const vec& c, int64 mod) : init(s), trans(c), mod(mod), m(s.size()) {}
LinearRecurrence(const vec& s, int64 mod, bool is_prime = true) : mod(mod)
{
vec A;
if (is_prime)
A = BerlekampMassey(s, mod);
else
A = ReedsSloane(s, mod);
if (A.empty()) A = { 0 };
m = A.size() - 1;
trans.resize(m);
for (int i = 0; i < m; ++i)
{
trans[i] = (mod - A[i + 1]) % mod;
}
std::reverse(trans.begin(), trans.end());
init = { s.begin(), s.begin() + m };
}
int64 calc(int64 n)
{
if (mod == 1) return 0;
if (n < m) return init[n];
vec v(m), u(m << 1);
int msk = !!n;
for (int64 m = n; m > 1; m >>= 1) msk <<= 1;
v[0] = 1 % mod;
for (int x = 0; msk; msk >>= 1, x <<= 1)
{
std::fill_n(u.begin(), m * 2, 0);
x |= !!(n & msk);
if (x < m)
u[x] = 1 % mod;
else
{ // can be optimized by fft/ntt
for (int i = 0; i < m; ++i)
{
for (int j = 0, t = i + (x & 1); j < m; ++j, ++t)
{
u[t] = (u[t] + v[i] * v[j]) % mod;
}
}
for (int i = m * 2 - 1; i >= m; --i)
{
for (int j = 0, t = i - m; j < m; ++j, ++t)
{
u[t] = (u[t] + trans[j] * u[i]) % mod;
}
}
}
v = { u.begin(), u.begin() + m };
}
int64 ret = 0;
for (int i = 0; i < m; ++i)
{
ret = (ret + v[i] * init[i]) % mod;
}
return ret;
}
vec init, trans;
int64 mod;
int m;
};
const int mod = 1e9;
typedef long long ll;
ll Pow(ll a, ll n, ll mod)
{
ll t = 1;
for (; n; n >>= 1, (a *= a) %= mod)
if (n & 1) (t *= a) %= mod;
return t;
}
int main()
{
int n, m;
cin >> n >> m;
std::vector<long long> f = { 0, 1 };
//预处理 2*m+5项
for (int i = 2; i < m * 2; i++)
f.push_back((f[i - 1] + f[i - 2]) % mod);
for (auto& t : f) t = Pow(t, m, mod);
for (int i = 1; i < m * 2; i++)
f[i] = (f[i - 1] + f[i]) % mod;
LinearRecurrence solver(f, mod, false);
printf("%lld\n", solver.calc(n));
}
B、Quadratic equation
(update by 2019/9/9)
已知 、,求 x 和 y
可以求出 ,已知 ,可以通过求出 这个数字 是否是mod的二次剩余,若不是,一定无解,反之,有解。
由于 ,所以 ,所以需要分四种情况讨论。
// x^2 同余 n (%mod)
// 前提:mod 是奇质数,若mod是合数,分解成质数套ex_crt
#include <bits/stdc++.h>
#define ll long long
using namespace std;
ll power(ll a, ll b, ll c)
{
ll ans = 1;
while (b)
{
if (b % 2 == 1)
ans = (ans * a) % c;
b /= 2;
a = (a * a) % c;
}
return ans;
}
ll mod, w;//w是二次域的D值
struct Q_Field//二次域
{
ll x, y;
Q_Field operator*(Q_Field T)//二次域乘法重载
{
Q_Field ans;
ans.x = (x * T.x % mod + y * T.y % mod * w % mod) % mod;
ans.y = (x * T.y % mod + y * T.x % mod) % mod;
return ans;
}
Q_Field operator^(ll b)//二次域快速幂
{
Q_Field ans;
Q_Field a = *this;
ans.x = 1;
ans.y = 0;
while (b)
{
if (b & 1)
{
ans = ans * a;
b--;
}
b /= 2;
a = a * a;
}
return ans;
}
};
ll Legender(ll a)//求勒让德符号
{
ll ans = power(a, (mod - 1) / 2, mod);
if (ans + 1 == mod)//如果ans的值为-1,%p之后会变成p-1。
return -1;
else
return ans;
}
ll Getw(ll n, ll a)//根据随机出来a的值确定对应w的值
{
return ((a * a - n) % mod + mod) % mod;//防爆处理
}
ll Solve(ll n)
{
ll a;
if (mod == 2)//当p为2的时候,n只会是0或1,然后0和1就是对应的解
return n;
if (Legender(n) == -1)//无解
return -1;
srand((unsigned)time(NULL));
while (1)//随机a的值直到有解
{
a = (rand() % mod);
w = Getw(n, a);
if (Legender(w) == -1)
break;
}
Q_Field ans, res;
res.x = a;
res.y = 1;//res的值就是a+根号w
ans = res ^ ((mod + 1) / 2);
return ans.x;
}
int main()
{
mod = 1e9 + 7;
ll n, ans1, ans2, b, c, q, w;
int T;
scanf("%d", &T);
while (T--)
{
scanf("%lld%lld", &b, &c);
n = (b * b % mod - c * 4 % mod + mod) % mod;
if (n % mod == 0)
ans1 = ans2 = 0;
else
{
ans1 = Solve(n);
ans2 = mod - ans1;//一组解的和是p
}
if (ans1 == -1)
printf("-1 -1\n");
else
{
if (ans1 > ans2)
swap(ans1, ans2);
q = (b - ans1) / 2;
w = (b + ans1) / 2;
if (q >= 0 && q < mod && w >= 0 && w < mod && q * w % mod == c)
printf("%lld %lld\n", q, w);
else
{
q = (b - ans2) / 2;
w = (b + ans2) / 2;
if (q >= 0 && q < mod && w >= 0 && w < mod && q * w % mod == c)
printf("%lld %lld\n", q, w);
else
{
q = (b - ans1 + mod) / 2;
w = (b + ans1 + mod) / 2;
if (q >= 0 && q < mod && w >= 0 && w < mod && q * w % mod == c)
printf("%lld %lld\n", q, w);
else
{
q = (b - ans2 + mod) / 2;
w = (b + ans2 + mod) / 2;
if (q >= 0 && q < mod && w >= 0 && w < mod && q * w % mod == c)
printf("%lld %lld\n", q, w);
else
printf("-1 -1\n");
}
}
}
}
}
}
D、Knapsack Cryptosystem
给你36个数字,让你任选几个数字(每个数字最多只能用一次),问能不能给定的数字。
状压两个18,然后第一次状压存一下能获得的值,第二次判断给定的数字减去当前能获得的值是否在第一次中获得了。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll dp1[1 << 20], dp2[1 << 20];
ll c[40];
map<ll, ll> mp;
void f(ll x, int l)
{
int t = 0;
while (x)
{
printf("%lld", x % 2);
x /= 2;
t++;
}
for (int i = t + 1; i <= l; i++)
printf("0");
}
int main()
{
int n;
ll s;
scanf("%d %lld", &n, &s);
for (int i = 0; i < n; i++)
scanf("%lld", &c[i]);
int len1 = (1 << (n / 2)) - 1;
for (int i = 1; i <= len1; i++)
{
for (int j = 0; j < n / 2; j++)
{
if (i & (1 << j))
{
dp1[i] = dp1[i ^ (1 << j)] + c[j];
mp[dp1[i]] = i;
}
}
}
int len2 = n - (n / 2);
for (int i = 1; i <= (1 << len2) - 1; i++)
{
for (int j = 0; j < len2; j++)
{
if (i & (1 << j))
{
dp2[i] = dp2[i ^ (1 << j)] + c[j + (n / 2)];
if (mp[s - dp2[i]])
{
f(mp[s - dp2[i]], n / 2);
f(i, len2);
return 0;
}
}
}
}
}
E、All men are brothers
有n个人(1,2,3,4,n),m次操作
每次操作给定两个数字,表示这俩个人交朋友,问你在每次操作后,任取四个人,他们相互都不是朋友有多少种取法。
其中朋友的朋友也是朋友
1、所以题意可以抽象为有 n 个不同的数字,每次让两个数字变为一块,并查集,然后考虑两个数字变成相同对于答案的影响,就相当于现在不能取包含这两个数字,其他两个不同的情况
2、所以每次操作都要减去 num[a]*num[b]*任取两个数字不同
3、暴力算的话,每次操作都需要 O(n) ,显然会超时,所以我们可以在每次操作后维护一个取两个数字相同的方案数,就可以做到 O(1) 。
#include <bits/stdc++.h>
#define ll long long
#define sc scanf
#define pr printf
using namespace std;
int que[100005], num[100005];
void init()
{
for (int i = 0; i < 100005; i++)
que[i] = i, num[i] = 1;
}
int getf(int k)
{
return que[k] == k ? k : que[k] = getf(que[k]);
}
void merge(int a, int b)
{
int q = getf(a), w = getf(b);
que[q] = w;
num[w] += num[q];
}
int main()
{
init();
ll n, m;
sc("%lld%lld", &n, &m);
ll a = n, b = n - 1, c = n - 2, d = n - 3;
if (n % 4 == 0)
a /= 4;
else if (n % 4 == 1)
b /= 4;
else if (n % 4 == 2)
c /= 4;
else
d /= 4;
ll ans = a * b / 2 * c / 3 * d;
//temp维护的当前能取多少对两个相同的数字
ll temp = 0;
printf("%lld\n", ans);
while (m--)
{
int a, b;
sc("%d%d", &a, &b);
if (getf(a) != getf(b))
{
ll num1 = num[getf(a)];
ll num2 = num[getf(b)];
merge(a, b);
//其他数字个数
ll all = n - num1 - num2;
//ans减去取 a b 再另取两个不同的数字的合法的数量
//合法数量=任取-两个数字一样的情况
//两个数字一样的情况=所有-数字 a b 一样的情况
ans -= num1 * num2 * (all * (all - 1) / 2 - (temp - (num1 * (num1 - 1) / 2 + num2 * (num2 - 1) / 2)));
//更新所有两个数字一样的情况
temp = temp - (num1 * (num1 - 1) / 2 + num2 * (num2 - 1) / 2) + (num1 + num2) * (num1 + num2 - 1) / 2;
}
printf("%lld\n", ans);
}
}
H、Cutting Bamboos
主席树模板题,求所有大于 x 的值减去 x 的和。
二分枚举高度,主席树计算差值。
#include <bits/stdc++.h>
#define ll long long
#define imid int mid=(left+right)/2;
using namespace std;
const int MAXN = 200005;
const int logMAXN = 25;
const double eps = 1e-8;
double k;
struct node
{
int l;
int r;
ll cnt;
ll sum;
}tree[MAXN * logMAXN];
int root[MAXN], a[MAXN], t[MAXN], n, m, cnt;
ll sum[MAXN];
void init()
{
root[0] = 0;
tree[0].l = tree[0].r = tree[0].cnt = 0;
cnt = 1;
}
void build(int num, int& rot, int left, int right)
{
tree[cnt++] = tree[rot];
rot = cnt - 1;
tree[rot].cnt++;
tree[rot].sum += num;
if (left == right)
return;
imid;
if (num <= mid)
build(num, tree[rot].l, left, mid);
else
build(num, tree[rot].r, mid + 1, right);
}
double query(int pre, int nex, int L, int R, int left, int right)
{
if (L == left && right == R)
return (tree[nex].sum - tree[pre].sum) - (tree[nex].cnt - tree[pre].cnt) * k;
imid;
double ans = 0;
//要严格保证没有冗余区间
if (R <= mid)
return query(tree[pre].l, tree[nex].l, L, R, left, mid);
else if (L > mid)
return query(tree[pre].r, tree[nex].r, L, R, mid + 1, right);
else
return query(tree[pre].l, tree[nex].l, L, mid, left, mid)+ query(tree[pre].r, tree[nex].r, mid + 1, R, mid + 1, right);
}
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++)
{
scanf("%d", &a[i]);
sum[i] = sum[i - 1] + a[i];
}
init();
for (int i = 1; i <= n; i++)
{
root[i] = root[i - 1];
build(a[i], root[i], 1, 200000);
}
int ql, qr;
ll x, y;
while (m--)
{
scanf("%d%d%lld%lld", &ql, &qr, &x, &y);
double need = (double)(sum[qr] - sum[ql - 1]) * x / y;
double l = 0, r = 200000;
while (l + eps < r)
{
k = (l + r) / 2;
double res = query(root[ql - 1], root[qr], ceil(k), 200000, 1, 200000);
if (res > need)
l = k;
else
r = k;
}
printf("%.10lf\n", l);
}
}