Atcoder Regular Contest 106 題解 (A-F)
Pro A 106
-
題意:給定 \(n\) ,求滿足 \(3^a+5^b=n\) 的任意一組解。
-
資料範圍:\(n\leq 10^{18}\)
-
做法:
預處理出值在 \(10^{18}\) 次方以內的 \(3\) 的冪和 \(5\) 的冪然後直接列舉。
-
程式碼:
const unsigned long long MAX=1e18; using namespace std; int d3,d5; unsigned long long _3[100],_5[100],n; void Prework(){ _3[0]=_5[0]=1; for(int i=1;_3[i-1]<=MAX;d3=i-1,++i) _3[i]=_3[i-1]*3ll; for(int i=1;_5[i-1]<=MAX;d5=i-1,++i) _5[i]=_5[i-1]*5ll; } int main(){ Prework();r(n); for(int i=1;i<=d3;++i) for(int j=1;j<=d5;++j) if(_3[i]+_5[j]==n) printf("%d %d\n",i,j),exit(0); puts("-1"); return 0; }
Pro B Values
-
題意:給定一張 \(n\) 個點 \(m\) 條邊的圖,每個點有權值 \(a_i\) ,以及期望變成的權值 \(b_i\) 。每次你可以進行一次如下操作:選取一條邊 \(e:u\rightarrow v\) ,將 \(u\) 權值加一 \(v\) 的權值減一或者將 \(v\) 的權值減一 \(u\) 的權值加一。每條邊可以進行無限次操作。
-
資料範圍:\(n,m\leq 10^5,a_i,b_i\leq 10^9\)
-
做法:
在一個連通塊內的點點權是可以任意傳遞的,所以只需要判斷連通塊內的點的 \(\sum a\) 與 \(\sum b\)
-
程式碼:
const int N=2e5+5,M=2e5+5; using namespace std; int n,m,num; int vis[N],head[N],nt[M<<1],to[M<<1]; long long a[N],b[N],tota,totb; void Add(int x,int y){ ++num;nt[num]=head[x];head[x]=num;to[num]=y; ++num;nt[num]=head[y];head[y]=num;to[num]=x; } void DFS(int p){ vis[p]=1;tota+=a[p],totb+=b[p]; for(int i=head[p];i;i=nt[i]) if(!vis[to[i]]) DFS(to[i]); } int main(){ r(n),r(m); for(int i=1;i<=n;++i) r(a[i]); for(int i=1;i<=n;++i) r(b[i]); for(int i=1,x,y;i<=m;++i) r(x),r(y),Add(x,y); for(int i=1;i<=n;++i){ if(vis[i]) continue; tota=0;totb=0;DFS(i); if(tota!=totb) puts("No"),exit(0); } puts("Yes"); return 0; }
Pro C Solutions
-
題意:有一個題目:給定 \(n\) 個閉區間 \([l_i,r_i]\) ,求在滿足兩兩不相交的情況下選出最多的區間。Takahashi 寫了個程式:將 \(r_i\) 按升序排列後,每次從左往右能選就選;Aoki 寫了個程式:將 \(l_i\) 按升序排序後,每次從左往右能選就選。你需要構造這 \(n\) 個閉區間,使得 Takahashi 的答案比 Aoki 的答案恰好多 \(m\) 。無解輸出
-1
。 -
資料範圍:\(n\leq 10^5,-n\leq m \leq n,l_i,r_i\leq 10^9,l_i\neq r_j,l_i\neq l_j(i\neq j),r_i\neq r_j(i\neq j)\)
-
做法:
Takahashi 的做法是正確做法,所以 \(m\) 只能為正數。當 \(m\ge n-1\) 時易得不存在解(因為此時至少需要滿足 \(n\) 個區間均互不相交,這時 Aoki 也可以得出正解)。當 \(m\leq n-2\) 時,我們可以構造出 \(m+2\) 個區間,滿足:第一個區間 Aoki 必然會選,並且選完後就不能再選剩下的 \(m+1\) 個了;而 Takahashi 則會選擇 \(m+1\) 個區間而放棄第一個區間。至於剩下的區間,我們讓兩人都選擇就可以了。
-
程式碼:
int n,m; void Printres(int k){ int L=1e7; for(int i=1;i<=k;++i,L+=2) w(L),putchar(' '),w(L+1),putchar(10); } void Solve(int d){ puts("1 1000000"); int L=2; for(int i=1;i<=d+1;++i,L+=2) w(L),putchar(' '),w(L+1),putchar(10); Printres(n-(d+2)); } int main(){ r(n),r(m); if(n==1&&m==0) return 0*puts("1 1"); if(m<0||m>=n-1) return 0*puts("-1"); Solve(m); return 0; }
Pro D Powers
-
題意:給定 \(n,p\) 和長度為 \(n\) 的序列 \(\{a\}\) ,對於 \(\forall x\in[1,p]\) ,求下式的值:
\[\sum_{i=1}^{n-1}\sum_{j=i+1}^{n}(a_i+a_j)^x \pmod{998244353} \] -
資料範圍:\(2\leq n\leq 2\times 10^5,1\leq p\leq 300\)
-
做法:
對原式進行變形:
\[\begin{aligned} \sum_{i=1}^{n-1}\sum_{j=i+1}^{n}(a_i+a_j)^x&=\sum_{i=1}^{n}\sum_{j=1}^{n}(a_i+a_j)^x-2^x\sum_{i=1}^{n}a_i^x\\ &=\sum_{i=1}^{n}\sum_{j=1}^{n}\sum_{k=0}^{x}\pmatrix{x\\k} a_i^ka_j^{x-k}-\cdots\\ &=\sum_{k=0}^{x}\pmatrix{x\\k}\sum_{i=1}^{n}a_i^k\sum_{j=1}^{n}a_j^{x-k}-\cdots \end{aligned} \] 只要對於 \(k\in[1,p]\) 處理出 \(\sum\limits_{i=1}^{n} a_i^k\) 就行了。複雜度 \(\mathcal{O}(nk)\) 。
-
程式碼:
const int N=2e5+5,K=3e2+5,Mod=998244353,inv=(Mod+1)/2; using namespace std; int n,k; int a[N],bin[K],s[N][K],c[K][K]; void Prework(){ bin[0]=1; for(int i=1;i<=k;++i) bin[i]=bin[i-1]*2ll%Mod; c[0][0]=c[1][0]=1; for(int i=1;i<=k;++i,c[i][0]=1) for(int j=1;j<=i;++j) c[i][j]=(c[i-1][j-1]+c[i-1][j])%Mod; for(int i=1;i<=n;++i) s[i][0]=1; for(int i=1;i<=n;++i) for(int j=1;j<=k;++j) s[i][j]=1ll*s[i][j-1]*a[i]%Mod; for(int i=0;i<=k;++i) for(int j=2;j<=n;++j) s[j][i]=(s[j-1][i]+s[j][i])%Mod; } int solve(int d){ int res=0; for(int i=0;i<=d;++i) res=(res+1ll*c[d][i]*s[n][i]%Mod*s[n][d-i]%Mod)%Mod; return res; } int main(){ r(n),r(k); for(int i=1;i<=n;++i) r(a[i]); Prework(); for(int i=1;i<=k;++i){ int ans=solve(i); ans=(ans-1ll*bin[i]*s[n][i]%Mod+Mod)%Mod; w(1ll*ans*inv%Mod);putchar(10); } return 0; }
Pro E Medals
-
題意:有 \(n\) 個工人,每個工人都會工作 \(a_i\) 天,休息 \(a_i\) 天,再工作 \(a_i\) 天 \(\cdots\cdots\) 你現在需要給這 \(n\) 個工人每人發 \(k\) 個獎牌,但是你每天只能給一個人獎牌。請問你至少要多少天才能達成目標。
-
資料範圍:\(n\leq 18,k\leq 10^5,a_i\leq 10^5\)
-
做法:
考慮構建二分圖,左邊有 \(n\times k\) 個節點欽定為某個人的獎牌,右邊設有 \(x\) 個節點,第 \(i\) 個表示第 \(i\) 天。代表欽定為第 \(i\) 個人的獎牌的節點向第 \(i\) 個人的工作日連邊。原問題轉化為:\(x\) 至少為多少時,該二分圖有最大完備匹配。
Hall 定理:二分圖存在最大完備匹配的充要條件是與某一側的任意 \(k\) 個點相連的另一側的節點 \(\ge k\) 個。
左部節點相當於是 \(n\) 塊,每一塊的 \(k\) 個節點與右部相連的點集完全一致,所以可以一選俱選。因此,當我們選擇了 \(i\) 個人時,右部的節點必須有 \(\ge i\times k\) 個與之欽定的獎牌相連——換句話說,這 \(i\) 個人出現的天數的並集大小應 \(\ge i\times k\) 。考慮補集轉化 ,總天數為 \(x\) 時,任意 \(i\) 個人出現的天數的並集大小等於 \(x-\) 僅有剩下的 \(n-i\) 個人出現的天數。至於求僅有 \(x\) 個人出現的天數,我們可以先求出每一天的工作的人的集合,再用桶的方法求出每個集合工作的天數,再做一次子集和,就 OK 了。所以我們二分天數 \(x\) ,每次判定對於任意工作者的子集是否滿足 Hall 定理即可。複雜度 \(\mathcal{O}(n2^n\log{nk})\) 。
-
程式碼:
const int U=(1<<20),N=20,K=1e5+5,M=3*N*K; using namespace std; int n,k; int a[N],t[U],bit[U],work[M]; int main(){ r(n),r(k);int Max=n*k*3; for(int i=1;i<=n;++i) r(a[i]); for(int i=1;i<=n;++i) for(int j=1;j<=Max;++j) if((((j-1)/a[i])&1)==0) work[j]|=(1<<i-1); int l=n*k,r=Max,u=(1<<n)-1,ans=0; for(int i=1;i<=u;++i) bit[i]=bit[i&(i-1)]+k; while(l<=r){ int mid=l+r>>1; memset(t,0,sizeof t); for(int i=1;i<=mid;++i) ++t[work[i]]; for(int i=1;i<=n;++i){ int d=(1<<i-1)-1; for(int j=0;j<=u;++j){ int cur=(j&d)|((j&~d)<<1); t[cur|(1<<i-1)]+=t[cur]; } } bool flag=true; for(int i=1;i<=u&&flag;++i) flag&=((mid-t[i^u])>=(bit[i])); if(flag) ans=mid,r=mid-1; else l=mid+1; } w(ans); return 0; }
Pro F Figures
-
題意:有 \(n\) 個有標號的點,每個點規定度數至多為 \(a_i\) ,且這 \(a_i\) 個度數介面互不相同,求生成樹個數在 \(\text{mod } 998244353\) 意義下的值。
-
資料範圍:\(2\leq n\leq 10^5,1\leq a_i< 998244353\)
-
做法:
假設最後點 \(i\) 實際上的度數為 \(d_i+1\) ,那麼每次形成方案都會對答案造成的額外貢獻為 \(a_i^{\underline{d_i+1}}\) 。因為 prufer 序列與生成樹一一對應,所以最終答案應該等於
\[\sum_{\sum_{d_i}=n-2}\pmatrix{n-2\\d_1,d_2,\cdots,d_n}\prod_{i=1}^{n}a_i^{\underline{d_i+1}} \] 觀察式子可知,我們用生成函式時應使用指數型生成函式。設 \(F_k(x)\) 表示當度數至多為 \(k\) 時的生成函式(次數為 \(i\) 的項的係數為該點的度數為 \(i+1\) 時的貢獻),則有
\[\begin{aligned} F_k(x)&=\sum_{i=0}^{k-1}\frac{k^{\underline{i+1}}}{i!}x^i\\ &=\sum_{i=0}^{k-1}k\frac{(k-1)!}{i!(k-i-1)!}x^i\\ &=k\sum_{i=0}^{k-1}\pmatrix{k-1\\i}x^i\\ &=k(1+x)^{k-1} \end{aligned} \] 所以答案為
\[\begin{aligned} {[x^{n-2}]}\prod_{i=1}^{n}F_{a_i}(x)&=\prod_{i=1}^{n}a_i\times [x^{n-2}]\prod_{i=1}^{n}(1+x)^{a_i-1}\\ &=\prod_{i=1}^{n}a_i\times [x^{n-2}](1+x)^{\sum\limits_{i=1}^{n}(a_i-1)}\\ &=\prod_{i=1}^{n}a_i\times \pmatrix{\sum\limits_{i=1}^{n}(a_i-1)\\n-2}(n-2)!\\ &=\prod_{i=1}^{n}a_i(\sum_{i=1}^{n}(a_i-1))^{\underline{n-2}} \end{aligned} \] 可以做到 \(\mathcal{O}(n)\) 。
還有一種不用生成函式,而是使用構造的方法。我們假設每個點都有一個關鍵介面,剩下的都是普通介面。我們可以每次執行以下的步驟來生成一棵樹。
- 在所有的普通介面中選出來一個
- 在所有的關鍵介面中選出來一個未使用的且與之前選擇的普通介面未連通的關鍵介面
- 連線這兩個介面
執行 \(n-2\) 次操作以後,我們直接連線剩下的兩個關鍵介面,此時必然可以生成一棵樹。我們來考慮怎麼計算它的貢獻。假設操作從 \(0\) 編號,那麼準備第 \(k\) 次操作時的普通介面個數為 \(\sum\limits_{i=1}^{n}(a_i-1)-k\) ,關鍵介面的個數為 \(n-k-1\) 。我們在一切步驟開始之前需要列舉關鍵節點,這個的貢獻為 \(\prod\limits_{i=1}^{n}a_i\) 。除此之外,由於邊本來是無序的,而我們這麼做相當於強行使邊有序的加入了,所以要除以 \((n-1)!\) 。所以總答案為
\[\begin{aligned} &\frac{1}{(n-1)!}\prod_{i=1}^{n}a_i\prod_{i=0}^{n-3}(n-i-1)(\sum\limits_{j=1}^{n}(a_j-1)-i)\\ =&\frac{1}{(n-1)!}\prod_{i=1}^{n}a_i\prod_{i=0}^{n-3}(n-i-1)\prod_{i=0}^{n-3}(\sum_{j=1}^{n}(a_j-1)-i)\\ =&\frac{1}{(n-1)!}\prod_{i=1}^{n}a_i(n-1)!(\sum_{i=1}^{n}(a_i-1))^{\underline{n-2}}\\ =&\prod_{i=1}^{n}a_i(\sum_{i=1}^{n}(a_i-1))^{\underline{n-2}} \end{aligned} \] 得到的式子與之前的一致。【鼓掌】
-
程式碼:
const int N=2e5+5,Mod=998244353; using namespace std; int n,sum,ans=1; int main(){ r(n); for(int i=1,d;i<=n;++i) r(d),ans=1ll*ans*d%Mod,sum=(sum+d-1)%Mod; for(int i=0;i<n-2;++i) ans=1ll*ans*(sum+Mod-i)%Mod; w(ans); return 0; }