zoukankan      html  css  js  c++  java
  • ARC106 选做 (AtCoder Regular Contest 106)

    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) 为预处理每个时间点对应的状态的复杂度。

    upd:其实等价于二分图存在完美匹配,用 Hall 定理的话看出来解法挺 easy 的,看来是我 sb 了。

    (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 ;
    }
    
  • 相关阅读:
    JFinal框架
    shiro认证登录实现
    linux常用的命令
    zookeeper部署到linux操作步骤
    java的冒泡排序
    软件设计的七大原则
    vue知识点整理
    JVM垃圾回收算法
    Sql Server删库了怎么办?跑路是不可能跑路的
    Linux--find用法
  • 原文地址:https://www.cnblogs.com/Soulist/p/13872450.html
Copyright © 2011-2022 走看看