ARC106 选做

Posted soulist

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ARC106 选做相关的知识,希望对你有一定的参考价值。

ARC106 选做

ARC106D [* easy]

给定长度为 (N) 的数列 (A),对于 (X=1,2...K) 计算:

[sum_isum_j (A_i+A_j)^X[i<j] ]

(Nle 2 imes 10^5,Kle 300)

Solution

考虑二项式定理:

[egin{aligned} &sum_k inom{X}{k}sum_isum_j A_i^kA_j^{X-k}[i<j] \&sum_kinom{X}{k} frac{1}{2}igg(Big(sum_i A_i^kBig)Big(sum_j A_j^{X-k}Big)-sum_i A_i^kA_i^{X-k}igg) end{aligned}]

预处理 (sum A_i^k),设为 (S_k),答案可以表示为:

[egin{aligned} &frac{1}{2}sum_k inom{X}{k}igg(S_kS_{X-k}-sum_i A_i^kA_i^{X-k}igg) \&frac{1}{2}igg(sum_k S_kS_{X-k}inom{X}{k}-sum_isum_k A_i^kA_i^{X-k}inom{X}{k}igg) \&frac{1}{2}igg(sum_k S_kS_{X-k}inom{X}{k}-sum_i (2A_i)^Xigg) end{aligned}]

(mathcal O(NK)) 的预处理,然后 (mathcal O(K^2)) 的 count 即可,复杂度 (mathcal O(NK+K^2))

(Code:)

#include<bits/stdc++.h>
using namespace std ;
#define Next( i, x ) for( register int i = head[x]; i; i = e[i].next )
#define rep( i, s, t ) for( register int i = (s); i <= (t); ++ i )
#define drep( i, s, t ) for( register int i = (t); i >= (s); -- i )
#define re register
#define int long long
int gi() {
	char cc = getchar() ; int cn = 0, flus = 1 ;
	while( cc < ‘0‘ || cc > ‘9‘ ) {  if( cc == ‘-‘ ) flus = - flus ; cc = getchar() ; }
	while( cc >= ‘0‘ && cc <= ‘9‘ )  cn = cn * 10 + cc - ‘0‘, cc = getchar() ;
	return cn * flus ;
}
const int P = 998244353 ; 
const int N = 2e5 + 5 ; 
const int M = 300 + 5 ; 
int fpow(int x, int k) {
	int ans = 1, base = x ;
	while(k) {
		if(k & 1) ans = 1ll * ans * base % P ;
		base = 1ll * base * base % P, k >>= 1 ;
	} return ans ;
}
int n, m, C[M][M], a[N], S[M], f[M], I ; 
signed main()
{
	n = gi(), m = gi() ; 
	rep( i, 1, n ) a[i] = gi() ; 
	C[0][0] = 1 ; 
	rep( i, 1, m ) rep( j, 0, i ) 
		C[i][j] = (!j) ? 1 : (C[i - 1][j - 1] + C[i - 1][j]) % P ;
	S[0] = n, f[0] = n ; 
	rep( i, 1, n ) {
		int z = 1, t = 1 ;
		rep( j, 1, m ) 
			z = z * a[i] % P, t = t * (a[i]) * 2 % P, 
			S[j] = (S[j] + z) % P, f[j] = (f[j] + t) % P ; 
	}
	I = (P + 1) / 2 ;
	rep( i, 1, m ) {
		int Ans = 0 ; 
		rep( j, 0, i ) Ans = (Ans + C[i][j] * S[j] % P * S[i - j] % P) % P ;
		Ans = (Ans - f[i] + P) % P ;
		cout << Ans * I % P << endl ; 
	}
	return 0 ;
}

ARC106E [* easy]

(n) 个工人,第 (i) 个人第 ([1,A_i]) 天工作,第 ([A_i+1,2A_i]) 休息,然后 ([2A_i+1,3A_i]) 工作,依次类推。

你需要给这些工人发工资,使得所有工人都至少领到了 (K) 个硬币,规则是你每天可以选择一个在工作的工人发一枚硬币。

求最少多少天可以发完。

(Nle 18,Kle 10^5,A_ile 10^5)

Solution

我们发现答案的下界是 (NK)

我们发现我们存在一种 (2NK) 级的策略,就是一个人一个人的发完,显然这个策略的答案是 (2NK) 这个级别的。

当然,显然我们可以更优的给硬币,我们可以猜测答案的上界是 (2NK)

同时不难给出 (2NK) 的例子,即所有 (A_i) 相同。

考虑二分答案,然后这个模型非常像网络流问题,考虑暴力网络流建模:

  • 从源点 (S) 连向每天 (i) 一点流量。
  • 从每个人 (j o T)(K) 点流量。
  • 每天向这一天在工作的每个人连向 (infty) 的流量。

此时,如果这张图流量为 (NK) 就说明合法。

不难注意到有不少天 (i)(j) 的连边是相似的,我们可以将他们压缩到一起,因为本质不同的连边只有 (2^n) 种(即这一天连向所有人的可能的情况)

此时这张图为二分图,暴力 dinic 的复杂度为 (mathcal O(Msqrt{N})) 近似于 (ncdot 2^{frac{3}{2}n}) 这个级别。

我们肯定不是暴力网络流,考虑最大流等于最小割,我们考虑这张图的最小割。

我们发现这张二分图的 (T) 边的节点数只有 (N) 个,而 (S) 边有 (2^N) 个,同时 (S) 边的每个节点均为一个状态,它只会向 (T) 边此位为 (1) 的点连边。

考虑枚举 (T) 边的割边情况,此时另一边不用被割去的边仅有此边对应的状态为它的子集的情况。那么可以使用高维前缀和/FMT来进行预处理,然后取 (min) 来得到最小割即可,如果最小割为 (NK) 就说明合法。

复杂度为 (mathcal O(N^2K+N2^Nlog (NK)))

其中 (N^2K) 为预处理每个时间点对应的状态的复杂度。

(Code:)

#include<bits/stdc++.h>
using namespace std ;
#define Next( i, x ) for( register int i = head[x]; i; i = e[i].next )
#define rep( i, s, t ) for( register int i = (s); i <= (t); ++ i )
#define Rep( i, s, t ) for( register int i = (s); i < (t); ++ i )
#define drep( i, s, t ) for( register int i = (t); i >= (s); -- i )
#define re register
int gi() {
	char cc = getchar() ; int cn = 0, flus = 1 ;
	while( cc < ‘0‘ || cc > ‘9‘ ) {  if( cc == ‘-‘ ) flus = - flus ; cc = getchar() ; }
	while( cc >= ‘0‘ && cc <= ‘9‘ )  cn = cn * 10 + cc - ‘0‘, cc = getchar() ;
	return cn * flus ;
}
const int N = (1 << 18) + 5 ; 
const int M = 5e6 + 5 ; 
int n, K, A[20], lim, limit, sta[M] ; 
int f[N] ; 
int bit(int x) {
	return __builtin_popcount(x) ; 
}
bool check(int x) {
	limit = (1 << n) - 1 ; 
	rep( i, 0, limit ) f[i] = 0 ;
	rep( i, 1, x ) ++ f[sta[i]] ; 
	int ans = n * K ; 
	for(re int k = 1; k <= limit; k <<= 1)
	rep( i, 0, limit ) if(i & k) f[i] += f[i ^ k] ; 
	for(re int i = 0; i <= limit; ++ i) 
		ans = min( bit(i) * K + f[limit] - f[i], ans ) ; 
	return (ans == n * K) ; 
}
signed main()
{
	n = gi(), K = gi(), lim = 2 * n * K ; 
	Rep( i, 0, n ) A[i] = gi() ; 
	rep( i, 1, lim ) Rep( j, 0, n ) 
		if(!(((i - 1) / A[j]) & 1)) sta[i] |= (1 << j) ;  
	int l = 0, r = lim, ans = 0 ;
	while( l <= r ) {
		int mid = (l + r) >> 1 ;
		if(check(mid)) ans = mid, r = mid - 1 ;
		else l = mid + 1 ; 
	}
	cout << ans << endl ; 
	return 0 ;
}

ARC106F [* easy]

给定 (n) 个机器人,第 (i) 个机器人有 (d_i) 个接口。

你需要将机器人连接成树,方法为分别选定两个不同的机器人的一个未被选择的接口,然后连接这两个机器人。

求本质不同的树的数量,其中连接的接口不同视为树不同。

答案对 (998244353) 取模。

(Nle 2 imes 10^5,d_i<998244353)

Solution

考虑这 (n) 个机器人生成的树,假设此树上机器人 (i) 的度数为 (c_i),那么贡献为 (d_i^{underline{c_i}})(考虑给边标号,然后再任意排布)

现在从 prufer 序列的角度来考虑答案,由于每个点的度数都是出现次数 (+1),方便起见我们给答案乘以 (prod d_i),然后给 (d_i-1),现在需要统计长度为 (N-2) 的序列的所有贡献和。

使用 EGF 刻画答案,我们不难发现:

[prod(sum_k frac{x^kd_i^{underline{k}}}{k!})[x^{N-2}] imes (N-2)! ]

即为答案。

考虑前半部分:

[prodigg(sum_k x^kinom{d_i}{k}igg)=(1+x)^{sum d_i} ]

((1+x)^{k}[x^{z}]=inom{k}{z})

(D=sum d_i,Nleftarrow N-2,M=sum d_i),我们有答案即为:

[inom{D}{N} imes N! imes M=D^{underline{N}} imes M ]

这样就避免了分母为 (0) 的问题了。

(Code:)

#include<bits/stdc++.h>
using namespace std ;
#define Next( i, x ) for( register int i = head[x]; i; i = e[i].next )
#define rep( i, s, t ) for( register int i = (s); i <= (t); ++ i )
#define drep( i, s, t ) for( register int i = (t); i >= (s); -- i )
#define re register
#define int long long
int gi() {
	char cc = getchar() ; int cn = 0, flus = 1 ;
	while( cc < ‘0‘ || cc > ‘9‘ ) {  if( cc == ‘-‘ ) flus = - flus ; cc = getchar() ; }
	while( cc >= ‘0‘ && cc <= ‘9‘ )  cn = cn * 10 + cc - ‘0‘, cc = getchar() ;
	return cn * flus ;
}
const int P = 998244353 ;  
int fpow(int x, int k) {
	int ans = 1, base = x ;
	while(k) {
		if(k & 1) ans = 1ll * ans * base % P ;
		base = 1ll * base * base % P, k >>= 1 ;
	} return ans ;
}
int n, D, M ; 
signed main()
{
	n = gi() ; int x ; M = 1 ; 
	rep( i, 1, n ) x = gi(), D = (D + x - 1) % P, M = M * x % P ; 
	n -= 2 ; 
	rep( i, 1, n ) M = M * (D - i + 1) % P ; 
	cout << M << endl ;  
	return 0 ;
}

以上是关于ARC106 选做的主要内容,如果未能解决你的问题,请参考以下文章

MySort(选做)的实现

201621123062《java程序设计》第九周作业总结

20165201 课下作业第十周(选做)

20175325 实现mypwd(选做,加分)

第九次作业

数据结构-排序(选做) 20175204