zoukankan      html  css  js  c++  java
  • FJWC2020 Day4 题解

    T1

    Description

    已知 (g_0=a)(g_1=b),并且有

    [g_n=3g_{n-1}-g_{n-2}(n > 1) ]

    已知 (f_{n,0}=n),并且

    [f_{n,k}=f_{g_n,k-1}(k>0) ]

    给定 (a,b,n,k,p) 的值,请你求出 (f_{n,k}mod p) 的结果。

    (0 ≤ a, b < p)(1 ≤ T ≤ 1000)(1 ≤ n, p ≤ 10^9)(1 ≤ k ≤ 100)

    时空限制 (1s/512MB)

    Solution

    先考虑怎么求 (g_{g_n})

    (g_n) 可能很大,考虑求 (g_n) 的循环节。

    (F_n) 为斐波那契数列第 (n) 项,打表可以发现 (g_n=F_{2n}×b-F_{2(n-1)}×a)

    所以 (g) 的循环节跟斐波那契数列的一样。

    具体地,记斐波那契数列模 (p) 意义下的最小循环节为 (pi(p))

    (p=prod_{i=1}^wp_i^{a_i})(分解质因数),则:

    [pi(p)= ext{lcm}_{i=1}^w(h(p_i)p_i^{a_i-1}) ]

    其中

    [h(p)= egin{cases} 3& p=2\ 20& p=5\ p-1& p=±1pmod5\ 2p+2& p=±2pmod5\ end{cases}]

    证明太(我)复(也)杂(不)了(会),这里就不展开了。

    时间复杂度 (O(能过))

    Code

    #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('
    ');
    }
    
    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

    Description

    给定一棵 (n) 个点的树,点被编号为 (0 sim n-1)

    你可以进行 (k) 次操作,每次操作你可以删去当前树上的任意一条边,然后再加上一条边。你需要保证每次操作过后当前的图仍然是一棵树,并且每次删除一条边后可以加入和删除边相同的边。

    现在请你数出,在 (k) 次操作过后,一共会有多少种可能的树的形态。

    两棵树的形态不同,当且仅当存在一条边 ((x,y)),在其中一棵树中出现,在另一棵树中没有出现。

    (1 leq n leq 50)(0 leq k < n)

    时空限制 (1s/512MB)

    Solution

    题目所求即:有多少棵树满足至多 (k) 条边不在原树中。

    定义一条边 ((u,v)) 的权值:若 ((u,v)) 在原树中,权值为 (1),否则为 (x)

    我们要求的就是边权之积 (le x^k) 的生成树个数。

    众所周知,定义一棵生成树的权值为树边权之积,那么用矩阵树定理可以求出所有生成树的权值之和。

    在这题中,求出的权值之和是一个含 (x)(n-1) 次多项式,(x^i) 的系数就是权值为 (i) 的生成树个数,(x^0sim x^k) 的系数之和就是答案。

    发现不太容易直接求这个多项式,考虑 ( ext{lagrange}) 插值,即分别令 (x=1sim n),用矩阵树定理求出多项式的值。

    将求出的值全部代入 ( ext{lagrange}) 插值的公式,即可还原出所求多项式。

    时间复杂度 (O(n^4))

    Code

    #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

    Description

    给定一棵边带权的树,请你求出满足 (u)(v) 的最短简单路径上的所有边的边权的 (mathrm{gcd})(1) 的点对 ((u,v)(u<v)) 的个数。

    不仅如此,你还要处理 (Q) 次修改边权的操作,每次修改后,你都要输出当前局面的答案。

    (2 ≤ n ≤ 10^5)(Q ≤ 100)(1 ≤ w, x ≤ 10^6)

    时空限制 (2s/512MB)

    Solution

    首先,(ans=sum_{i=1}^{10^6}mu(i)f(i)),其中 (f(i)) 表示 (mathrm{gcd}mod i=0) 的路径条数。

    接着,把每条边 ((u,v,w)) 拆成 ((u,v,w_1),(u,v,w_2),dots,(u,v,w_k))

    其中 (w_1sim w_k)(w) 的所有满足 (mu e 0) 的约数。

    那么现在 (f(i)) 就是边权全部为 (i) 的路径条数。

    对于每条有被修改的边 (x),分别考虑时刻 (0sim Q) 中,(x) 的权值。

    也就是说把这 (Q+1) 个时刻的权值全部拆成约数,并在每条边上标记时刻。

    对于没有被修改的边 (x),把拆出的边标上时刻 (-1),表示始终存在。

    接下来,枚举边权 (v),计算权值为 (v) 的边对这 (Q+1) 个答案的贡献。

    具体地,维护一个可持久化并查集,同时维护一个变量 (sum),表示目前形成了多少条边权都是 (v) 的路径。

    枚举时刻 (t=-1sim Q)

    先把 (t=-1) 的边加入并查集,然后枚举 (lceil) 不存在边权为 (v) 的边 ( floor) 的时刻 (j)(ans[j]+=sum×mu(v))

    (tge 0) 时,每次把时刻为 (t) 的边加入并查集,计算对 (ans[t]) 的贡献后,再把这些边全部从并查集中删除。

    期望时间复杂度 (O((n+q)slog n)),其中 (s)(10^6) 以内的数的平均约数个数。

    Code

    #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('
    ');
    	fclose(stdin);
    	fclose(stdout);
    	return 0;
    }
    
  • 相关阅读:
    Educational Codeforces Round 20 D. Magazine Ad
    Educational Codeforces Round 20 C. Maximal GCD
    紫书第三章训练2 暴力集
    Educational Codeforces Round 20 B. Distances to Zero
    Educational Codeforces Round 20 A. Maximal Binary Matrix
    紫书第三章训练1 D
    紫书第一章训练1 D -Message Decoding
    HAZU校赛 Problem K: Deadline
    Mutual Training for Wannafly Union #8 D
    紫书第三章训练1 E
  • 原文地址:https://www.cnblogs.com/cyf32768/p/12324132.html
Copyright © 2011-2022 走看看