1. 程式人生 > 其它 >Tour【單調佇列】

Tour【單調佇列】

題意

1號點為首都,其餘共n-1個點,每個點有一個上游城市\(f_i\),存在一條單向道路\(f_i \ -> \ i\)
現在可以從任何一個點出發開始沿著道路遊歷,且可以在任意城市終止
你擁有使用特權的機會,使用一次特權即可以從跳到另一個點開始繼續遊歷
問 使用0,1,2,。。。,n-1次遊歷機會分別最多能訪問多少個城市?

題解

首先,出於貪心的思想,先訪問城市最長的鏈,因為這樣增加一次遊歷機會可以訪問的城市是最多的。
我們可以首先用dfs求出從每個點開始往下訪問 能到達的最多城市數,將它們儲存進優先佇列(按照從該城市出發能訪問城市多的城市優先順序高的順序)。
訪問了佇列頭的元素後,標記所有的它這一條往下訪問的路上的點,表示這些點已經被訪問過,以後不需要再次訪問

code

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 200005,mod = 10007;
int n,m,t,x;
vector<int>e[N];
int f[N];  //從i出發最長的路徑 
int son[N];  //i最長路徑下的兒子 
bool vis[N];
int ans[N];
struct node{
	int id;
	int dis;
	bool operator < (const node a) const{
		return dis<a.dis;
	}
}; 

void dfs(int now){
	int mx=0;
	for(int i=0;i<e[now].size();i++){
		int x=e[now][i];
		dfs(x);
		if(f[x]>mx){
			mx=f[x];
			son[now]=x;
		}
	}
	f[now]=mx+1;
}

int main(){
	scanf("%d",&n);
	for(int i=2,x;i<=n;i++){
		scanf("%d",&x);
		e[x].push_back(i);
	}
	dfs(1);

	priority_queue<node,vector<node>,less<node> >q;
	
	for(int i=1;i<=n;i++) q.push({i,f[i]});
	
	int cnt=0;
	while(!q.empty()){
		int now=q.top().id;
		q.pop();
		if(vis[now]){
			continue;
		}
		vis[now]=1;
		if(!cnt) ans[cnt]=f[now];
		else ans[cnt]=ans[cnt-1]+f[now];
		cnt++;
		while(son[now]){
			vis[son[now]]=1;
			now=son[now];
		}
	}
	for(int i=0;i<n;i++){
		if(i&&ans[i-1]==n) ans[i]=n;
		printf("%d\n",ans[i]);
	}
	return 0;
}