Atcoder Dwango Programming Contest 6th 題解
\(A\) \(Falling\) \(Asleep\)
\(description:\)
給你一個歌單\(,\) \(n\)首歌的歌名\(s_i\)和播放持續時間\(t_i\) \(,\)
讀入一個歌名\(st,\)要求在歌單中算出在這首曲子之後所有曲子的播放時間總和\(.\)
\(solution:\)
暴力即可\(.\) 時間複雜度 \(O(n + \sum|s_i|)\)
\(code:\)
#include <bits/stdc++.h> using namespace std; inline int read(){ int x = 0; char c = getchar(); while (!isdigit(c)) c = getchar(); while (isdigit(c)) x = x * 10 + c - '0',c = getchar(); return x; } template <typename T> void read(T &x){ x = 0; int f = 1; char ch = getchar(); while (!isdigit(ch)) {if (ch == '-') f = -1; ch = getchar();} while (isdigit(ch)) {x = x * 10 + ch - '0'; ch = getchar();} x *= f; } inline void write(int x){if (x > 9) write(x/10); putchar(x%10+'0'); } int n; string st,s[500]; int t[500]; int main(){ cin >> n; for (int i = 1; i<= n; ++i) cin >> s[i] >> t[i]; cin >> st; int id = -1; long long ans = 0; for (int i = 1; i <= n; ++i) if (s[i] == st) id = i; for (int i = id+1; i <= n; ++i) ans += t[i]; cout << ans << endl; return 0; }
\(B\) \(Fusing\) \(Slimes\)
\(description :\)
有\(n(n≤10^5)\)塊石子\(,\)每個石子有其初始座標\(x_i.\)
每次你會從中隨機選出不在最右邊的一堆\(,\)將這一堆移動到 它右邊最靠近它的一堆石子的位置\(,\)並把這兩堆石子合併為一堆\(.\)
求出所有情況下\(,\)石子移動距離的總和 \(,\) 即期望乘以 \((n-1)!\) \(.\)
答案對\(P = 1e9 + 7\) 取模\(.\)
\(solution :\)
考慮一段距離\(d_i = x_{i+1} - x_i\)
定義 $dp[n] = $ 一段距離之前有\(n\)
不難得出 \(dp[i] = dp[i-1] + 1/i\)
然後就可以直接計算出每一段距離對答案的貢獻了\(.\)
時間複雜度\(O(n).\)
\(code :\)
#include <bits/stdc++.h> using namespace std; inline int read(){ int x = 0; char c = getchar(); while (!isdigit(c)) c = getchar(); while (isdigit(c)) x = x * 10 + c - '0',c = getchar(); return x; } template <typename T> void read(T &x){ x = 0; int f = 1; char ch = getchar(); while (!isdigit(ch)) {if (ch == '-') f = -1; ch = getchar();} while (isdigit(ch)) {x = x * 10 + ch - '0'; ch = getchar();} x *= f; } inline void write(int x){if (x > 9) write(x/10); putchar(x%10+'0'); } const int P = 1e9 + 7,N = 500050; int n,fac[N],inv[N]; int x[N]; int f[N]; long long ans,now; int main(){ int i; read(n); for (i = 1; i <= n; ++i) read(x[i]); for (fac[0] = i = 1; i <= n; ++i) fac[i] = 1ll*fac[i-1]*i%P; for (inv[0] = inv[1] = 1,i = 2; i <= n; ++i) inv[i] = 1ll*inv[P%i]*(P-P/i)%P; for (i = 1; i <= n; ++i){ int prob; prob = inv[i]; f[i] = 1ll*prob*(f[i-1]+1) % P; f[i] += 1ll*(P+1-prob)*f[i-1] % P; f[i] %= P; } ans = 0; for (i = 1; i <= n-1; ++i){ int dist = x[i+1] - x[i]; ans = (ans + 1ll*dist*f[i]%P)%P; } cout << 1ll*fac[n-1]*ans%P << endl; return 0;
\(C\) \(Cookie\) \(Distribution\)
$description : $
有 \(n\) 個人 \(,\) 在 \(k\) 天中你要給他們發糖果 \(.\)
讀入 \(a_1 .. a_k,\) 其中 \(a_i\) 表示第\(i\)天會從\(n\)個人當中均勻隨機選出 \(a_i\) 個人 \(,\) 並給他們每人發一顆糖 \(.\)
記\(c_i\)為第\(i\)個人發到的糖果個數\(,\)求\(\Pi c_i\)的期望\(.\)
\(n<=10^3,k<=20\)
\(solution:\)
求 \(\prod c_i\) 相當於求 從 \(n\) 個人中每個人手裡選一顆糖的方案數
考慮列舉 \(x_i\) 表示從第 \(i\) 個人手裡選的糖果是從哪一天來的 \(.\)
\(x_i\) 是什麼並不重要\(,\)重要的是\(cnt2_i :\) 表示有多少\(x_j\) \(=\) \(i\)
這種選法對答案的貢獻是
\[\large \prod^{k}_{i=1} C^{a_k-cnt2_k}_{n-cnt2_k} \times \prod_{i=1}^{k} (cnt2_i!)^{-1} \times n! \]
然後用一個\(O(nk^2) dp\)就可以解決這個問題了\(.\)
\(code :\)
#include <bits/stdc++.h>
using namespace std;
inline int read(){
int x = 0; char c = getchar();
while (!isdigit(c)) c = getchar();
while (isdigit(c)) x = x * 10 + c - '0',c = getchar();
return x;
}
template <typename T> void read(T &x){
x = 0; int f = 1; char ch = getchar();
while (!isdigit(ch)) {if (ch == '-') f = -1; ch = getchar();}
while (isdigit(ch)) {x = x * 10 + ch - '0'; ch = getchar();}
x *= f;
}
inline void write(int x){if (x > 9) write(x/10); putchar(x%10+'0'); }
const int N = 1050,K = 20 + 5,P = 1e9 + 7;
int n,k,a[K];
int fac[N],nfac[N],inv[N];
inline int C(int n,int m){ return (n<m||n<0||m<0) ? 0 : 1ll*fac[n]*nfac[m]%P*nfac[n-m]%P; }
int t[K][N];
int dp[K][N];
int main(){
int i,j,e;
read(n),read(k); for (i = 1; i <= k; ++i) read(a[i]);
inv[0] = fac[0] = nfac[0] = inv[1] = fac[1] = nfac[1] = 1;
for (i = 2; i <= n; ++i){
fac[i] = 1ll*fac[i-1]*i%P;
inv[i] = 1ll*(P-P/i)*inv[P%i]%P;
nfac[i] = 1ll*nfac[i-1]*inv[i]%P;
}
for (i = 1; i <= k; ++i)
for (j = 0; j <= n; ++j) t[i][j] = 1ll*C(n-j,a[i]-j)*nfac[j]%P;
dp[0][0] = 1;
for (i = 1; i <= k; ++i)
for (j = 0; j <= n; ++j){
dp[i][j] = 0;
for (e = 0; e <= j; ++e) dp[i][j] = (dp[i][j] + 1ll * t[i][e] * dp[i-1][j-e]) % P;
}
int ans = 1ll * dp[k][n] * fac[n] % P;
cout << ans << endl;
return 0;
}
\(D\) \(Arrangement\)
\(description :\)
給你一張圖\(G,\) \(G\)有\(n\)個點\(,\) \(n*(n-2)\) 條有向邊\(.\)
圖\(G\)的補圖是一個所有點出度\(=1\) \((\) 可能有自環 \()\) 的有向圖\(.\)
求出一條字典序最小的哈密爾頓路徑\(,\)如果不存在則輸出\(-1.\)
\(n<=10^5\)
\(solution :\)
首先如果規模足夠小\((n<=8)\)就可以直接暴力\(O(n!)\)
然後我們考慮對於\(n\)更大的情況來處理答案\(.\)
記$cnt_i = $ 當前的圖中\(,\) 有多少\(a_j = i\)
如果存在一個點\(p\)滿足\(cnt_p = n-1\)那麼我們必須選這個點作為路徑的第一個點\(.\)
如果不存在這樣的一個點\(,\)我們就可以選擇當前的點當中編號最小的點\(.\)
然後問題就變成了一個規模為\(n-1\)的子問題\(,\)只是多了一個開頭不能為某個數的限制\(.\)
那麼我們就可以寫一個支援 查詢\(cnt\)最大值\(,\) \(cnt\)單點修改 和 查詢當前點集的編號最小者\(,\) 刪除一個點的資料結構\(,\) 再寫一個\(O(n!)\)的暴力即可\(.\)
複雜度\(O(8! +nlogn)\)
\(code:\)
#include <bits/stdc++.h>
using namespace std;
inline int read(){
int x = 0; char c = getchar();
while (!isdigit(c)) c = getchar();
while (isdigit(c)) x = x * 10 + c - '0',c = getchar();
return x;
}
template <typename T> void read(T &x){
x = 0; int f = 1; char ch = getchar();
while (!isdigit(ch)) {if (ch == '-') f = -1; ch = getchar();}
while (isdigit(ch)) {x = x * 10 + ch - '0'; ch = getchar();}
x *= f;
}
inline void write(int x){if (x > 9) write(x/10); putchar(x%10+'0'); }
const int N = 100050;
int n,a[N];
namespace subtask0{
int ans[100],vis[100]; bool ok;
int p[100];
inline void chk(){
for (int i = 2; i <= n; ++i) if (a[ans[i-1]] == ans[i]) return;
ok = 1; for (int i = 1; i <= n; ++i) p[i] = ans[i];
return;
}
inline void dfs(int dep){
if (ok) return;
if (dep > n){ chk(); return; }
for (int i = 1; i <= n; ++i) if (!vis[i]){
vis[i] = 1,ans[dep] = i;
dfs(dep+1);
vis[i] = 0;
}
}
inline void solve(){
ok = 0; memset(vis,0,sizeof(vis)); memset(ans,0,sizeof(ans)); dfs(1);
if (!ok){ puts("-1"); return; }
for (int i = 1; i <= n; ++i) cout << p[i] << ((i<n) ? (' '):('\n'));
}
}
int mx[N<<2],mxi[N<<2],data[N<<2],cnt[N];
inline void Build(int o,int l,int r){
if (l^r){
int mid = l+r>>1; Build(o<<1,l,mid); Build(o<<1|1,mid+1,r);
mx[o] = max(mx[o<<1],mx[o<<1|1]); data[o] = min(data[o<<1],data[o<<1|1]);
mxi[o] = mxi[ (mx[o<<1]>mx[o<<1|1]) ? (o<<1) : (o<<1|1) ];
return;
}
mx[o] = cnt[l]; data[o] = (mx[o] >= 0) ? (l) : (n+1); mxi[o] = l;
}
inline int Ask(int o,int l,int r,int p){
if (l==r) return mx[o];
int mid = l+r>>1; return (p<=mid) ? Ask(o<<1,l,mid,p) : Ask(o<<1|1,mid+1,r,p);
}
inline void Add(int o,int l,int r,int p,int v){
if (l==r){ mx[o]=v; data[o] = (mx[o] >= 0) ? (l) : (n+1); return; }
int mid = l+r>>1; if (p<=mid) Add(o<<1,l,mid,p,v); else Add(o<<1|1,mid+1,r,p,v);
mx[o] = max(mx[o<<1],mx[o<<1|1]); data[o] = min(data[o<<1],data[o<<1|1]);
mxi[o] = mxi[ (mx[o<<1]>mx[o<<1|1]) ? (o<<1) : (o<<1|1) ];
}
inline int Nowv(){ return data[1]; }
inline int Query(int pos){ if (pos==0) return -1; return Ask(1,1,n,pos); }
inline void Modify(int x,int v){ cnt[x] = v; Add(1,1,n,x,v); }
int ans[N];
int vis[N],nowc[N],lc;
int qwq;
inline void chk(){
for (int i = qwq; i <= n; ++i) if (ans[i]==a[ans[i-1]]) return;
for (int i = 1; i <= n; ++i) write(ans[i]),putchar((i<n)?(' '):('\n'));
exit(0);
}
inline void dfs(int dep){
if (dep>n){ chk(); return; }
for (int i = 1; i <= lc; ++i) if (!vis[i]){
vis[i] = 1;
ans[dep] = nowc[i];
dfs(dep+1);
vis[i] = 0;
}
}
inline void solve_force(int l,int r){
qwq = l;
for (int i = 1; i <= n; ++i) if (Query(i) >= 0) nowc[++lc] = i,vis[lc] = 0;
dfs(l);
}
inline bool unproperty(){
for (int i = 1; i <= n; ++i) cnt[i] = 0;
for (int i = 1; i <= n; ++i) ++cnt[ans[i]];
for (int i = 1; i <= n; ++i) if (cnt[i] != 1) return 1;
for (int i = 2; i <= n; ++i) if (ans[i] == a[ans[i-1]]) return 1;
return 0;
}
int main(){
int i,banp,ret,siz,p;
int pp,val;
read(n);
for (i = 1; i <= n; ++i) read(a[i]);
for (i = 1; i <= n; ++i) if (a[i]==i) a[i]=0;
for (i = 1; i <= n; ++i) ++cnt[a[i]];
// if (n==2){ puts("-1"); return 0; }
if (n<=8){ subtask0::solve(); return 0; }
Build(1,1,n);
for (banp = 0,siz = n,i = 1; i <= n; ++i,--siz){
if (siz <= 6){
solve_force(i,n);
}
banp = a[ans[i-1]];
if (banp != 0 && (ret=Query(banp)) >= 0){
Modify(banp,-1);
if (mx[1] == siz-1){
p = mxi[1];
ans[i] = p;
Modify(ans[i],-1);
}
else{
p = data[1];
ans[i] = p;
Modify(ans[i],-1);
}
pp = a[ans[i]];
if ((val=Query(pp))>=0){ --val; Modify(pp,val); }
Modify(banp,ret);
}
else{
if (mx[1] == siz-1){
p = mxi[1];
ans[i] = p;
Modify(ans[i],-1);
}
else{
p = data[1];
ans[i] = p;
Modify(ans[i],-1);
}
pp = a[ans[i]];
if ((val=Query(pp))>=0){ --val; Modify(pp,val); }
}
}
if (unproperty()){ puts("-1"); return 0; }
for (i = 1; i <= n; ++i) write(ans[i]),putchar((i<n)?(' '):('\n'));
}
\(E\) \(Span\) \(Covering\)
\(description:\)
有\(n\)條長度為\(l_i\)的線段\(.\)
你要把這些線段放到一個長為\(X\)的座標軸上 \((\) \(n <=100\) \(X <= 500\) \()\)
並且滿足一個條件\(:\) 不能覆蓋到外面\(,\)也不能有地方沒有被任何一條線段覆蓋到\(.\)
求方案數\(.\)
\(solution:\)
首先把\(l_i\)從大到小排序\(.\)
考慮一個\(dp:\)
$f[i][j][k] = $ 前\(i\)個區間組成了\(j\)個相鄰的開區間\(,\)其總長度為\(k\)的方案數\(.\)
轉移有三種\(:\)
\(1.\) 新開一個區間\(;(j->j+1)\)
\(2.\) 把當前的某一個區間和我新加進去的區間合併為一個區間\(;(j->j)\)
\(3.\) 把兩個相鄰的區間通過我新加進去的區間合併成一個區間\(.(j->j-1)\)
其中第\(1\)種轉移的長度是確定的\(,\)為\(k + len\)
第\(2\) \(3\)種轉移的長度是需要列舉的\(.\)
看起來複雜度是\(O(n^2X^2),\)但是在列舉到\(i\)的時候\(,\)只有\(k*l_i <= j\)的狀態是有用的\(.\)
所以複雜度為\(O(???\) 能過 \()\) \((\) 好像是 \(O(nX^2)?\) \()\)
\(code:\)
#include <bits/stdc++.h>
using namespace std;
inline int read(){
int x = 0; char c = getchar();
while (!isdigit(c)) c = getchar();
while (isdigit(c)) x = x * 10 + c - '0',c = getchar();
return x;
}
template <typename T> void read(T &x){
x = 0; int f = 1; char ch = getchar();
while (!isdigit(ch)) {if (ch == '-') f = -1; ch = getchar();}
while (isdigit(ch)) {x = x * 10 + ch - '0'; ch = getchar();}
x *= f;
}
inline void write(int x){if (x > 9) write(x/10); putchar(x%10+'0'); }
const int N = 100 + 5,M = 1050,P = 1e9 + 7;
int n,m;
int f[N][M],g[N][M];
inline void upd(int &x,int y){ x+=y; x>=P?x-=P:0; x<0?x+=P:0; }
inline void DP(int L){
int i,j,k;
// cout <<"DP " << endl;
// for (i = 1; i <= n; ++i,cout << endl)
// for (j = 1; j <= m; ++j) cout <<f[i][j] << ' ';
for (i = 1; i <= n; ++i)
for (j = 1; j <= m; ++j) g[i][j] = f[i][j],f[i][j] = 0;
for (i = 1; i <= n; ++i)
for (j = 1; j <= m; ++j) if (g[i][j]>0){
upd(f[i+1][j+L],1ll*(i+1)*g[i][j]%P);
upd(f[i][j],1ll*g[i][j]*(P+j-i*(L-1))%P);
for (k = 1; k < L; ++k) upd(f[i][j+k],2ll*g[i][j]%P*i%P);
for (k = 0; k < L-1; ++k) upd(f[i-1][j+k],1ll*g[i][j]*(L-k-1)%P*(i-1)%P);
}
}
int a[N];
int main(){
int i,j;
int rm;
read(n),read(m); for (i = 1; i <= n; ++i) read(a[i]),++a[i]; rm = m; m += 1;
sort(a+1,a+n+1); reverse(a+1,a+n+1);
memset(f,0,sizeof(f)); f[1][a[1]] = 1;
for (i = 2; i <= n; ++i) DP(a[i]);
// cout <<"DP " << endl;
// for (i = 1; i <= n; ++i,cout << endl)
// for (j = 1; j <= m; ++j) cout <<f[i][j] << ' ';
int ans = 0;
for (i = 1; i <= 1; ++i) ans += f[i][rm+i],ans %= P;
cout << ans << endl;
return 0;
}