洛谷9月月賽 題解(模擬+最短路+期望DP+期望DP)
可能是距離AK最近的一次,但終究是錯付了QAQ
-------------------
T1 子弦
題目大意:給定一個字串,問出現最多的非空子串的個數。
唬人題。直接統計每個字母出現的個數即可。時間複雜度$O(n)$
程式碼:
#include<bits/stdc++.h> using namespace std; string s; int cnt[30],ans; int main() { cin>>s; for (int i=0;i<s.length();i++) cnt[s[i]-'a']++; for (int i=0;i<26;i++) ans=max(ans,cnt[i]); cout<<ans; return 0; }
T2 雷雨
題目大意:給定一個$n*m$的方格圖,每個格子內有元素$A_{i,j}$。現在給定一個起點和兩個終點,問從起點到兩個終點最短路並集的最小值。
設起點為$s$,終點分別為$t1,t2$。假設$t1≠t2$,那麼最短路的並集上一定有一個“岔路口”。這個岔路口到起點的距離和到兩個終點的距離之和一定是最小的;假設$t1=t2$,那麼圖上只有一條最短路,此時這個岔路口即為終點。
所以分別跑三次最短路,然後找到與起點還有兩個終點距離和的最小值即可。時間複雜度$O(nm+n\log n)$。
程式碼:
#include<cstdio> #include<iostream> #include<queue> #include<cstring> #define int long long using namespace std; const int inf=1e18; const int dx[]={0,-1,1,0,0}; const int dy[]={0,0,0,-1,1}; int n,m,a,b,c,dis[1000005][3],vis[1000005],map[1005][1005],ans=inf; struct node { int pos,dis;bool operator < (const node &x) const { return x.dis<dis; } }; priority_queue<node> q; inline int get_pos(int x,int y) { return (x-1)*m+y; } inline int read() { int x=0,f=1;char ch=getchar(); while(!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();} while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();} return x*f; } inline void bfs(int opt) { memset(vis,0,sizeof(vis)); for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) dis[get_pos(i,j)][opt]=inf; if (opt==0) dis[get_pos(1,a)][0]=map[1][a],q.push((node){get_pos(1,a),map[1][a]}); if (opt==1) dis[get_pos(n,b)][1]=map[n][b],q.push((node){get_pos(n,b),map[n][b]}); if (opt==2) dis[get_pos(n,c)][2]=map[n][c],q.push((node){get_pos(n,c),map[n][c]}); while(!q.empty()) { int now=q.top().pos;q.pop(); if (vis[now]) continue; vis[now]=1; int x=now/n+1,y=now%n; if (!y) x--,y=m; for (int i=1;i<=4;i++) { int xx=x+dx[i],yy=y+dy[i]; if (xx>=1&&xx<=n&&yy>=1&&yy<=m) { if (dis[get_pos(xx,yy)][opt]>dis[now][opt]+map[xx][yy]) { dis[get_pos(xx,yy)][opt]=dis[now][opt]+map[xx][yy]; if (!vis[get_pos(xx,yy)]) q.push((node){get_pos(xx,yy),dis[get_pos(xx,yy)][opt]}); } } } } } signed main() { n=read();m=read();a=read();b=read();c=read(); for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) map[i][j]=read(); bfs(0);bfs(1);bfs(2); for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) ans=min(ans,dis[get_pos(i,j)][0]+dis[get_pos(i,j)][1]+dis[get_pos(i,j)][2]-2*map[i][j]); printf("%lld",ans); return 0; }
T3 夢原
題目大意:給定一棵樹,根節點為$1$。除根節點外每個結點等概率選擇$[\max(i-k,1),i-1]$內的一個節點作為父節點。每個結點有一個權值$a_i$。現有刪除操作:每次選擇一個結點權值均大於$0$的連通塊,操作過後連通塊內所有結點權值減$1$;每次都按最優策略進行操作。問操作的期望次數。
考慮到編號為$i$的結點的父親的編號只會比$i$小,在$[1,i]$內怎樣操作都不會對後面的結點產生影響,即沒有後效性,所以考慮DP。設$f_i$表示對前$i$個結點進行操作的期望次數。考慮結點$i$和它父親$fa$之間的關係。如果$a_i>a_{fa}$,那麼在某個時刻$i$會與前面的結點斷開連線,這時需要操作的次數是$f_{i-1}+a_i-a_{fa}$;如果$a_i\leq a_{fa}$,那麼可以直接按照之前的策略進行操作即可,$a_i$會在某個時刻先變成$0$,此時操作次數是$f_{i-1}$。所以有轉移(假設此時$k$已經合法):
$f_i=k*f_{i-1}+ \frac{\sum\limits_{j=i-k}^{i-1} a_i-a_j}{k}$