ARC106 選做
ARC106D [* easy]
給定長度為 \(N\) 的數列 \(A\),對於 \(X=1,2...K\) 計算:
\(N\le 2\times 10^5,K\le 300\)
Solution
考慮二項式定理:
預處理 \(\sum A_i^k\),設為 \(S_k\),答案可以表示為:
\(\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 刻畫答案,我們不難發現:
即為答案。
考慮前半部分:
\((1+x)^{k}[x^{z}]=\binom{k}{z}\)。
設 \(D=\sum d_i,N\leftarrow N-2,M=\sum d_i\),我們有答案即為:
這樣就避免了分母為 \(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 ;
}