2019 牛客 多校赛 第九场

slove  1/10

rank  419

补题   4/10

--------------------------------------------------------

Link

A、The power of Fibonacci

给两个整数 n,m,求斐波那契的前N项的M次方的和

ex杜教BM,存板子

#include 
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;

	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 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.size() + m);
				int64 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.size() + m);
				int64 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 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> 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 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 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)\%p=b(x*y)\%p=c,求 x 和 y

可以求出 (x-y)^2 \equiv b^2-4*a*c (\%mod),已知 b^2-4*a*c,可以通过求出 这个数字 是否是mod的二次剩余,若不是,一定无解,反之,有解。

由于 (x+y)\%p=b,所以 x+y=b||x+y+mod=b,所以需要分四种情况讨论。

// x^2 同余 n (%mod)
// 前提:mod 是奇质数,若mod是合数,分解成质数套ex_crt
#include 
#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
using namespace std;
#define ll long long
ll dp1[1 << 20], dp2[1 << 20];
ll c[40];
map 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 
#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 
#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);
	}
}

 

你可能感兴趣的:(2019,牛客多校赛)