1. 程式人生 > 實用技巧 >洛谷9月月賽 題解(模擬+最短路+期望DP+期望DP)

洛谷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}$