D - Operator Precedence
求一個長度為 \(2n\) 的序列 \(a_{2n}\) 滿足條件 \((a_1 × a_2)+(a_3 × a_4)+\ldots+(a_{2n-1} × a_{2n})=a_1×(a_2+a_3)×\ldots×(a_{2n-2}+a_{2n-1})×a_{2n}\)
solution
構造題顯然找特殊規律。
考慮到乘法構造難度大於加法,可以從乘法開始考慮。
注意到不要求 \({a_i}\) 各不相等且 \(a_1×1×1×\ldots ×1×a_{2n}==a_1×a_{2n}\) ,考慮能不能令 \(a_{2n-2}+a_{2n-1}\) 都為 1。
顯然任意欽定 \(a_{2n-2}+a_{2n-1}\) 的值之後,只剩未知數 \(a_1\) 和 \(a_{2n}\)。
再欽定其一解另一即可。
同理 \(a_1×0×0×\ldots ×0×a_{2n}==0\) ,考慮能不能令 \(a_{2n-2}+a_{2n-1}\) 都為 0。
解法同上。
G - Snake Move
給定 \(n × m\) 地圖上的一條長度為 \(k\) 的貪吃蛇。每次操作可以控制貪吃蛇移動一步或者縮短一格蛇身。
對於每個位置,求從初始狀態出發最少需要多少次操作使得蛇頭到達該處。
Solution
甩個題解吧
每個操作都可以等價看作初始蛇身必然縮短 \(1\),並且同時控制蛇頭移動一步或者不移動,因此蛇頭到達每個位置的時間越早越好。
最短路建圖,相鄰格子連代價 \(1\) 的邊。
如果一個位置在初始狀態下不被蛇身佔據,考慮按 Dijkstra 最短路演算法第一次訪問該點的時刻,蛇頭一定不會與蛇身相交。
否則,對於初始狀態下第 \(i (2 ≤ i ≤ k)\) 節蛇身所處的位置,顯然必須操作 \(k − i\) 次後才能訪問該點。因此鬆弛該點時,需要將最短路長度與
\(k − i\) 取 max。(這一部分沒想到)
注:對於取 max 操作的 \(k\) 個點,使用另一個佇列升序記錄每個狀態。那麼 Dijkstra 演算法的瓶頸——找距離最小的點可以透過比較兩個佇列的隊首
\(\text{O}(1)\) 得到。
void Dij(){
priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > > q;
q.push(make_pair(0,rt));Dis[rt]=0;memset(Dis,0x3f,sizeof Dis);
while(!q.empty()){
int u=q.top().second;q.pop();
if(vis[u]) continue;vis[u]=1;
for(int i=head[u];i;i=e[i].nxt){
int to=e[i].to;
if(dis[to]){
if(Dis[to]>Dis[u]+k-dis[to]+1)
Dis[to]=Dis[u]+k-dis[to]+1,
q.push(make_pair(Dis[to],to));
}
else{
if(Dis[to]>Dis[u]+1)
Dis[to]=Dis[u]+1,
q.push(make_pair(Dis[to],to));
}
}
}
}
int main(){
n=read();m=read();k=read();
for(int i=1,x,y,tmp;i<=k;i++){
x=read();y=read();
tmp=(x-1)*m+y;dis[tmp]=i;
if(i==1) rt=tmp;
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
char s;cin>>s;
if(s=='.'){
for(int t=0;t<4;t++){
int tx=i+dx[t],ty=j+dy[t];
if(tx<0||tx>=n||ty<0||ty>=m||s=='#') continue;
add((i-1)*m+j,(tx-1)*m+ty);
}
}
}
}
Dij();
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(Dis[(i-1)*m+j]!=0x3f) ans=(ans+qu(Dis[(i-1)*m+j],2))%Mod;
}
}
return 0;
}
H - Sugar Sweet II
給出 \(n\) 個按隨機順序發生的事件,每個事件可以描述為:若 \(a_i<a_{b_i}\),則 \(a_i\) 增加 \(w_i\)。
求每個數的期望。
Solution
先看官方題解:
要求的就是每個事件發生的機率。事件的依賴關係構成一棵基環樹。
如果 \(a_i < a_{b_i}\),則事件 \(i\) 必然發生。
若 \(a_i ≥ a_{b_i} + w_{b_i}\),則事件 \(i\) 必然不發生。
否則,事件 \(i\) 發生當且僅當事件 \(b_i\) 在事件 \(i\) 之前發生。
則對這樣子的事件而言,其發生的機率為 \(\frac{1}{L_i !}\),其中 \(L_i\) 為基環樹上 \(i\) 到最近的,必定發生的祖先事件的機率。
如果離 \(i\) 最近的是一個必定不發生的事件,則事件 \(i\) 發生的機率為 \(0\)。
使用任何 \(\text{O}(n)\) 或者 \(\text{O}(n \text{log} n)\) 的演算法處理出 \(L_i\) 即可。
為什麼這麼算呢,\(L_i!\) 其實就是 \(A_{L_i}^{L_i}\)。
即假如 C 必定發生,且 B 依賴 C ,A 依賴 B。如果 A 發生,那麼在最終發生的時間排列中一定存在形如 C…B…A 的子序列,所以在算機率時要除以 3 的全排列。
void init(){
fact[0]=infact[0]=1;
for (int i=1; i<=500000; i++ ){
fact[i]=fact[i-1]*i%mod;
infact[i]=infact[i-1]*qmi(i,mod-2) % mod;
}
}
void solve() {
for(int i=0;i<n;i++) cin>>a[i];
for(int i=0;i<n;i++) cin>>b[i],b[i]--;
for(int i=0;i<n;i++) cin>>w[i];
for (int i=0;i<n;i++){
if (a[i]<a[b[i]]) ans[i]=a[i]+w[i],f[i]=1;
else if (a[i]>=a[b[i]]+w[b[i]]) ans[i]=a[i];
else {g[i]=b[i];rd[b[i]]++;}
}
for (int i=0;i<n;i++) {if(!rd[i]) tp.pb(i);}
for (int i=0; i<tp.size(); i++) {
int u=tp[i];cir[u]=0;int v=g[u];
if (v==-1) continue;rd[v]--;
if (!rd[v]) tp.pb(v);
}
for (int i=0;i<n;i++)
if (cir[i]) ans[i]=a[i];
for (int i=tp.size()-1;i>=0;i--) {
int u=tp[i];int v=g[u];
if (v==-1) dep[u] = 1;
else if (cir[v]) {
cir[u]=1;
ans[u]=a[u];
}
else if (f[v]){
dep[u]=dep[v] + 1;
ans[u]=a[u]+w[u]*c(n,dep[u])%mod*fact[n-dep[u]]%mod*infact[n]%mod;
f[u]=1;
}
else ans[u]=a[u];
}
for(int i=0;i<ans.size();i++) cout<<x%mod<<" \n";
}
int main() {
cin>>t;init();
while(t--){
cin>>n;
solve();
}
return 0;
}
J - Mysterious Tree
一棵樹,可能是鏈或者菊花,每次詢問一條邊存在性,確定是鏈還是菊花。
一共有 \(\lceil{\frac{n}{2}}\rceil +3\) 次詢問機會。
Solution
顯然對於一條邊 \((x,y)\),如果兩端點的度數全為 \(1\) 則為鏈,否則為菊花圖。
因此我們只需要找出一個存在的邊並詢問即可。
再考慮詢問策略。
如果我們找到了一條存在的邊 \((x,y)\),因為無法統計每個點的度數,因此只能再分別找出與 \(x,y\) 相連的點。
我們隨機找一個點 \(p\),若圖為菊花圖則 \((p,x),(p,y)\) 必定有一個存在,反之可能不存在。若不存在則直接判斷。
若存在(我們假設 \((p,x)\) 存在),則可以再隨機找一個點 \(q\),若 \((q,x)\) 也存在則必定為菊花圖,反之為鏈圖。
因此我們透過 \((x,y)\) 來判斷只需不超過 \(3\) 次提問。
那顯然剩下的 \(\lceil{\frac{n}{2}}\rceil\) 次提問是為了找出 \((x,y)\)。
我們發現,若圖為菊花圖,那麼形如 \((i,i+1)\) 的邊必然存在,反之可能不存在。而進行全部詢問次數正好與剩餘次數相等。
因此不斷詢問,若存在則按上述步驟解,否則直接判斷為鏈圖即可。
M - V-Diagram
給一個 V 圖,求一個連續子序列平均值最大的 V 圖。
V 圖指先嚴格單調減再嚴格單調增的序列。
Solution
我們考慮從完整序列的左右分別刪除元素。
考慮左側,若 \(a_1\) 刪除後更優,因為 \(a_1>a_2\) 則 \(a_2\) 刪除也會更優,因此左側應刪除到 \(a_{i-1}\)
右側同理。
因此答案為 \([1,i-1],[i+1,n]\) 或者 \([1,n]\)。