ARC106 選做 (AtCoder Regular Contest 106)


ARC106 選做

ARC106D [* easy]

給定長度為 \(N\) 的數列 \(A\),對於 \(X=1,2...K\) 計算:

\[\sum_i\sum_j (A_i+A_j)^X[i<j] \]

\(N\le 2\times 10^5,K\le 300\)

Solution

考慮二項式定理:

\[\begin{aligned} &\sum_k \binom{X}{k}\sum_i\sum_j A_i^kA_j^{X-k}[i<j] \\&\sum_k\binom{X}{k} \frac{1}{2}\bigg(\Big(\sum_i A_i^k\Big)\Big(\sum_j A_j^{X-k}\Big)-\sum_i A_i^kA_i^{X-k}\bigg) \end{aligned}\]

預處理 \(\sum A_i^k\),設為 \(S_k\),答案可以表示為:

\[\begin{aligned} &\frac{1}{2}\sum_k \binom{X}{k}\bigg(S_kS_{X-k}-\sum_i A_i^kA_i^{X-k}\bigg) \\&\frac{1}{2}\bigg(\sum_k S_kS_{X-k}\binom{X}{k}-\sum_i\sum_k A_i^kA_i^{X-k}\binom{X}{k}\bigg) \\&\frac{1}{2}\bigg(\sum_k S_kS_{X-k}\binom{X}{k}-\sum_i (2A_i)^X\bigg) \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\) 個硬幣,規則是你每天可以選擇一個在工作的工人發一枚硬幣。

求最少多少天可以發完。

\(N\le 18,K\le 10^5,A_i\le 10^5\)

Solution

我們發現答案的下界是 \(NK\)

我們發現我們存在一種 \(2NK\) 級的策略,就是一個人一個人的發完,顯然這個策略的答案是 \(2NK\) 這個級別的。

當然,顯然我們可以更優的給硬幣,我們可以猜測答案的上界是 \(2NK\)

同時不難給出 \(2NK\) 的例子,即所有 \(A_i\) 相同。

考慮二分答案,然后這個模型非常像網絡流問題,考慮暴力網絡流建模:

  • 從源點 \(S\) 連向每天 \(i\) 一點流量。
  • 從每個人 \(j\to T\)\(K\) 點流量。
  • 每天向這一天在工作的每個人連向 \(\infty\) 的流量。

此時,如果這張圖流量為 \(NK\) 就說明合法。

不難注意到有不少天 \(i\)\(j\) 的連邊是相似的,我們可以將他們壓縮到一起,因為本質不同的連邊只有 \(2^n\) 種(即這一天連向所有人的可能的情況)

此時這張圖為二分圖,暴力 dinic 的復雜度為 \(\mathcal O(M\sqrt{N})\) 近似於 \(n\cdot 2^{\frac{3}{2}n}\) 這個級別。

我們肯定不是暴力網絡流,考慮最大流等於最小割,我們考慮這張圖的最小割。

我們發現這張二分圖的 \(T\) 邊的節點數只有 \(N\) 個,而 \(S\) 邊有 \(2^N\) 個,同時 \(S\) 邊的每個節點均為一個狀態,它只會向 \(T\) 邊此位為 \(1\) 的點連邊。

考慮枚舉 \(T\) 邊的割邊情況,此時另一邊不用被割去的邊僅有此邊對應的狀態為它的子集的情況。那么可以使用高維前綴和/FMT來進行預處理,然后取 \(\min\) 來得到最小割即可,如果最小割為 \(NK\) 就說明合法。

復雜度為 \(\mathcal O(N^2K+N2^N\log (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\) 取模。

\(N\le 2\times 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}]\times (N-2)! \]

即為答案。

考慮前半部分:

\[\prod\bigg(\sum_k x^k\binom{d_i}{k}\bigg)=(1+x)^{\sum d_i} \]

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

\(D=\sum d_i,N\leftarrow N-2,M=\sum d_i\),我們有答案即為:

\[\binom{D}{N}\times N!\times M=D^{\underline{N}}\times 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 ;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM