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;
    }
    
  • 相关阅读:
    CRM详细介绍网址
    element上传多图片
    多图片上传WebForm
    MemCached原理
    Redis的原理
    Ninject用法详解
    SQLsugar和unity 的用法
    json中的转义字符的处理
    fiddler
    orm多表关系
  • 原文地址:https://www.cnblogs.com/cyf32768/p/12324132.html
Copyright © 2011-2022 走看看