• 11.13 考试总结


    wdnmd,真就都不会做嗷。

    T1 graph

    Desprition

    cwbc 想要随机生成一个特殊的 (n) 个节点的有向图,每个节点有且仅有一条出 边,已知生成该图时,每个点的出边指向每个点(包括自己)的概率是相同的, 现 在要你求出该有向图的弱连通分量的期望个数。 你只需要给出该期望值乘以 (n^n) 并对 998244353 取模的结果即可。

    弱连通分量:在一张有向图中,将所有有向边变为无向边后,每个连通块称为个弱连通分量 ((nleq 10^7)).

    solution

    神仙计数题。

    首先我们会发现生成的图是个基环树森林,关于这个图有一个性质,每个弱联通分量有且仅有一个环。

    于是我们求出环的数量就可以得到弱联通分量的数量。

    考虑枚举一下每个环的大小。

    对于点数为 (i) 的环,我们从 (n) 个点中选出 (i) 个点来组成这个环共有 (C_{n}^{i}) 中选法。

    对于这 (i) 个点,要向让他们组成一个为 大小为 (i) 的环,那么第一个点可以 和 剩下的 (i-1) 个点相连。

    第二个点和剩下的 (n-2) 个点相连 .....以此类推第 (n) 个点只能与第一个点相连。

    也就是说每个点不能和之前已经连过的点在连边,因此方案数为 ((i-1)!).

    对于剩下的 (n-i) 的点,显然这些点可以对所有的点相连,方案数位为 (n^{n-i})

    综上答案为: (ans = displaystylesum_{i=1}^{n}C_{n}^{i} imes (i-1)! imes n^{n-i})

    在除以图的数量 (n^n) 就可以得到最后的期望。

    可以线性求逆元,将时间复杂度降为 (O(n))

    Code

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    #define int long long
    const int p = 998244353;
    const int N = 1e7+10;
    int n,ans,base[N],jz[N],inv[N];
    inline int read()
    {
    	int s = 0,w = 1; char ch = getchar();
    	while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
    	while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();}
    	return s * w;
    }
    int ksm(int a,int b)
    {
    	int res = 1;
    	for(; b; b >>= 1)
    	{
    		if(b & 1) res = res * a % p;
    		a = a * a % p;
    	}
    	return res;
    }
    void YYCH()
    {
    	jz[0] = inv[0] = 1; base[0] = 1;
    	for(int i = 1; i <= n; i++) jz[i] = jz[i-1] * i % p;
    	inv[n] = ksm(jz[n],p-2);
    	for(int i = n-1; i >= 1; i--) inv[i] = inv[i+1] * (i+1) % p;
    	for(int i = 1; i <= n; i++) base[i] = base[i-1] * n % p;
    }
    int C(int n,int m)
    {
    	return jz[n] * inv[m] % p * inv[n-m] % p;
    }
    signed main()
    {
    	freopen("graph.in","r",stdin);
    	freopen("graph.out","w",stdout);
    	n = read(); YYCH();
    	for(int i = 1; i <= n; i++)
    	{
    		ans = (ans + C(n,i) * jz[i-1] % p * base[n-i] % p);
    	}
    	printf("%lld
    ",ans);
    	fclose(stdin); fclose(stdout);
    	return 0;
    }
    
    

    T2 jewelry

    Desprition

    给你一个 (n) 个节点的树,让你求出每个大小为 (k) 的联通块中点编号中位数的最大值。

    (n,kleq 2000) 保证 (k) 为奇数。

    solution

    考试的时候打了个链的暴力就走了,正解一点思路都没有。

    考虑一下二分答案 ,设答案为 (mid),我们可以求出每个大小为 (k) 的联通块中比 (mid) 大的个数 (cnt)

    如果 (cnt >= lfloor {kover 2} floor) 说明我们的答案较小,反之较大。

    然后问题就转换为了怎么求出大小为 (k) 的联通块中比 (mid) 大的数的个数。

    (f[x][i]) 表示在以 (x) 为根的子树中,选 (x) 且大小为 (i) 的联通块中比 (mid) 大的数最多有多少个。

    转移则有 $f[x][i+j] = max(f[x][i+j],f[x][i] + f[to][j]) $

    初始化 (f[x][0] = 0, f[x][1] = (x >= mid)).

    注意枚举 (i) 的时候要从 (1) 开始循环,因为 (x) 这个点必须要选。

    直接dp的复杂度是 (O(n^3)) 的,考虑每个点只用枚举到 (siz[x]) 就行,这样复杂度就降为 (O(n^2))

    总复杂度 (O(n^2logn)).

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    const int N = 3010;
    int n,k,tot,L,R,ans,u,v;
    int head[N],siz[N],f[N][N];
    struct node
    {
    	int to,net;
    }e[100010];
    inline int read()
    {
    	int s = 0,w = 1; char ch = getchar();
    	while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
    	while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();}
    	return s * w;
    }
    void add(int x,int y)
    {
    	e[++tot].to = y;
    	e[tot].net = head[x];
    	head[x] = tot;
    }
    void dfs(int x,int fa)
    {
    	siz[x] = 1;
    	for(int i = head[x]; i; i = e[i].net)
    	{
    		int to = e[i].to;
    		if(to == fa) continue;
    		dfs(to,x);
    		for(int j = min(k,siz[x]); j >= 1; j--)//每个点只能选一次,所以倒序循环
    		{
    			for(int u = 0; u <= siz[to]; u++)
    			{
    				f[x][j + u] = max(f[x][j + u], f[x][j] + f[to][u]);
    			}
    		}
    //		cout<<x<<" "<<f[x][k]<<endl;
    		siz[x] += siz[to];
    	} 
    }
    bool judge(int mid)
    {
    	memset(f,0,sizeof(f));
    	for(int i = 1; i <= n; i++) f[i][1] = i >= mid ? 1 : 0; 
    	dfs(1,1);
    	for(int i = 1; i <= n; i++)
    	{
    		if(f[i][k] >= k/2+1) return 1;
    	}
    	return 0;
    }
    int main()
    {
    	freopen("jewelry.in","r",stdin);
    	freopen("jewelry.out","w",stdout);
    	n = read(); k = read();
    	for(int i = 1; i <= n-1; i++)
    	{
    		u = read(); v = read();
    		add(u,v); add(v,u);
    	}
    	L = 1, R = n;
    	while(L <= R)
    	{
    		int mid = (L + R)>>1;
    		if(judge(mid)) 
    		{
    			L = mid + 1;
    			ans = mid;
    		}
    		else R = mid - 1;
    	}
    	printf("%d
    ",ans);
    	fclose(stdin); fclose(stdout);
    	return 0;
    }
    
    

    T3 labyrinth

    Desprition

    有一个 (n*m) 的网格,起点在 第 (sx)(sx) 列,终点在 第 (tx)(tx) 列,每个格子上的数字为 (a[i][j]).每次可以跳到同一行或者同一列的某个格子,花费为起跳点和终点之间所有格子上数字的最小值,请你求出从起点到终点的最小总花费。

    (n,mleq 8000,n*mleq 100000,a[i][j]leq 1000).

    solution

    只打了个 (O(n^2m+m^2)) 和所有 (a[i][j]) 相等的暴力。

    正解好像要建虚点,不会爪八了。

    T4 joseph

    Desprition

    约瑟夫游戏的规则是这样的:(n) 个人围成一圈,从 (1) 号开始依次报数,当报 到 (m) 时, 报 (1、2、...、m-1) 的人出局,下一个人接着从 (1) 开始报,保证 ((n-1))((m-1)) 的倍数。最后剩的一个人获胜。 YJC 很想赢得游戏,但他太笨了,他想让你帮他算出自己应该站在哪个位置 上。

    (n,mleq 2^{63}-1,保证 (n-1) 是 (m-1) 的倍数)

    solution

    正解好像是 (dp) ,但我不会写,我直接找规律做的QAQ。

    假设当前有 (n) 个人,直到第 (n) 个人报完停止一轮。

    那么下一轮的报数顺序可以写成 (n,n-1,n-2...n-nmod m,m,2m,3m,4m...{nover m}m).

    我们可以继续往下递归,考虑怎么有下一层必胜的位置 (tmp),推出这一层必胜的位置。

    (tmp leq nmod m) ,显然只会是上一轮后面的那几个数,所以直接返回 (n-tmp+1)

    反之则在上一轮的位置为 (km) 的位置,其中 $k = tmp- n mod m $ .

    自己可以画画图手动理解 一下。

    递归边界 (n=1, return 1)。 复杂度 O(玄学)。

    Code

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    #define int long long
    int n,m;
    inline int read()
    {
    	int s = 0,w = 1; char ch = getchar();
    	while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
    	while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();}
    	return s * w;
    }
    int slove(int n)
    {
    	if(n == 1) return 1;
    	int tmp = slove(n/m+n%m);
    	if(tmp <= n % m) return n - tmp + 1;
    	else return m * (tmp - n % m); 
    }
    signed main()
    {
    //	freopen("joseph.in","r",stdin);
    //	freopen("joseph.out","w",stdout);.
    	n = read(); m = read();
    	printf("%lld
    ",slove(n));
    	fclose(stdin); fclose(stdout);
    	return 0;
    }
    /*
    461168601842738905 3
    */
    
  • 相关阅读:
    JS中级二
    JS中级一
    JS入门八
    JS入门七
    JS入门六
    JS入门五
    JS入门四
    JS入门三
    JS入门二
    JS入门1
  • 原文地址:https://www.cnblogs.com/genshy/p/13971597.html
Copyright © 2020-2023  润新知