BUPT 2024 Spring Training #3(ICPC2023 杭州站)Ag覆盤

KnightL發表於2024-03-22

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]\)

相關文章