BuringStraw

BuringStraw

洛谷|P4281 [AHOI2008]緊急集合 / 集会

洛谷 | P4281 [AHOI2008] 緊急集合 / 集会#

問題の説明#

楽しい島には「緊急集合」という非常に面白いゲームがあります。島には N 個の待機ポイントが散らばっており、N-1 本の道路がそれらをつなげています。各道路は特定の 2 つの待機ポイントを結び、これらの道路を通じてすべての待機ポイントにアクセスできます。道路を通って 1 つのポイントから別のポイントに移動するには、1 ゲームコインが必要です。

ゲームに参加する人は 3 人 1 組で、最初はすべての人が任意に各待機ポイントに散らばっています(各ポイントには同時に複数の人が待機できます)。各人は十分なゲームコイン(道路の使用料を支払うため)、地図(待機ポイント間の道路接続状況を示す)および対話機(同じグループのメンバーと連絡を取るため)を持っています。集合の合図が鳴った後、各グループのメンバーは迅速に連絡を取り合い、自分のグループのすべてのメンバーがどの待機ポイントにいるかを把握し、N 個の待機ポイントの中から集合地点を迅速に決定します。集合にかかる費用が最も少ないグループがゲームの勝者となります。

小可可と彼の友人はあなたをこのゲームに招待し、あなたが集合地点を選ぶ役割を担います。賢いあなたはこのタスクを達成し、小可可がゲームに勝つのを助けることができるでしょうか?

入力出力フォーマット#

入力フォーマット:#

最初の行には 2 つの正整数 N と M(N<=500000、M<=500000)が空白で区切られて表示されます。これは待機ポイントの数(待機ポイントは 1 から N まで番号が付けられています)と、賞を得るために完了する必要がある集合の回数を示します。続いて N-1 行があり、各行には 2 つの正整数 A と B が空白で区切られて表示され、A 番と B 番の待機ポイントの間に道路があることを示します。次に M 行があり、各行には 3 つの正整数が表示され、特定の集合の前に小可可、小可可の友人、そしてあなたがいる待機ポイントの番号を示します。

出力フォーマット:#

合計で M 行あり、各行には 2 つの数 P,C が空白で区切られて表示されます。i 行目は i 回目の集合地点が P 番の待機ポイントに選ばれ、集合にかかる総費用が C ゲームコインであることを示します。

入力出力サンプル#

入力サンプル #1:

6 4  
1 2  
2 3  
2 4 
4 5
5 6
4 5 6
6 3 1
2 4 4 
6 6 6

出力サンプル #1:

5 2
2 5
4 1
6 0

説明#

ヒント:

40% のデータでは N<=2000、M<=2000
100% のデータでは、N<=500000、M<=500000

この問題は非常に難しいです!!

プロセス#

最初に考えたのはその 3 つのポイントの LCA を求めることでしたが、結果的に WA になりました。

左哥

~~ 同じくサンプルのコードですが、どうして私は 1 つのポイントも通過できなかったのか(滑稽) ~~

その後、この大佬の解法を見て、プロセスは原文にあり、結論は:

そのポイントは LCA (a,b),LCA (b,c),LCA (a,c) の中で深さが最も大きい

費用は deep (a)+deep (b)+deep (c)? 最LCA の深さ?最LCA の深さ?2(実際には a,b,c の 3 つの深さを引くことになります。なぜなら 2 つの LCA は同じだからです)

「木の上で任意の 3 点 t1,t2,t3 を選ぶと、LCA (t1,t2),LCA (t1,t3),LCA (t2,t3) の中には必ず 2 つが等しい。」

その後、洛谷で通過し、一中 oj では TLE の 2 ポイントが出ました。

最後に速読を使い、inline を追加しましたが、まだ通過できませんでした。

コード#

(このコードは速読を使用していません)

#include<bits/stdc++.h>
using namespace std;

const int MAXN=500000*2+5;
int n,m;

struct tu{
	private:
		struct ed{
			int to,nex;
		} e[MAXN];
		int head[MAXN],newp,siz,root,H,f[MAXN][20];
		int depth[MAXN];
		bool bfsed;
		
	public:
		inline void clear(void){
			memset(e,0,sizeof(e));
			memset(head,0,sizeof(head));
			memset(f,0,sizeof(f));
			memset(depth,0,sizeof(depth));
			newp=0;
			siz=0;
			root=1;
			bfsed=0;
		}
		
		inline void vAdd(int p1,int p2){
			++newp;
			e[newp].to=p2;
			e[newp].nex=head[p1];
			head[p1]=newp;
		}
		
		inline void resize(int s){
			siz=s;
			H=(int)(1.0*log(siz)/log(2)+0.5);
		}
		
		inline int size(void){
			return siz;
		}
		
		inline int lca(int p1,int p2){
			if(!bfsed){
				bfs();
			}
			
			if(depth[p1]<depth[p2]){
				swap(p1,p2);
			}
			
			for(int i=H;i>=0;--i){
				if(depth[f[p1][i]]>=depth[p2]){
					p1=f[p1][i];
				}
			}
			
			if(p1==p2)return p1;
			
			for(int i=H;i>=0;--i){
				if(f[p1][i]!=f[p2][i]){
					p1=f[p1][i];
					p2=f[p2][i];
				}
			}
			return f[p1][0];
		}
		
		inline void solve(int x,int y,int z){
			int a,b,c;
			a=lca(x,y);
			b=lca(y,z);
			c=lca(x,z);
			int ans1,ans2;
			if(depth[a]>=depth[b] && depth[a]>=depth[c]){
				ans1=a;
			}
			
			else if(depth[b]>=depth[a] && depth[b]>=depth[c]){
				ans1=b;
			}
			
			else if(depth[c]>=depth[a] && depth[c]>=depth[b]){
				ans1=c;
			}
			int pt1=depth[x]-depth[a];
			int pt2=depth[y]-depth[b];
			int pt3=depth[z]-depth[c];
			ans2=pt1+pt2+pt3;
			printf("%d %d\n",ans1,ans2);
		}
		
		inline void bfs(void){
			queue<int> q;
			q.push(root);
			depth[root]=1;
			while(!q.empty()){
				int x=q.front();
				q.pop();
				for(int i=head[x];i;i=e[i].nex){
					int y=e[i].to;
					
					if(depth[y])continue;
					
					depth[y]=depth[x]+1;
					
					f[y][0]=x;
					
					for(int j=1;j<=H;++j){
						f[y][j]=f[f[y][j-1]][j-1];
					}
					
					q.push(y);
				}
			}
			bfsed=1;
		}
		
		
};

tu a;

int main(void){
	scanf("%d%d",&n,&m);
	a.clear();
	a.resize(n);
	for(int i=1;i<n;++i){
		int p1,p2;
		scanf("%d%d",&p1,&p2);
		a.vAdd(p1,p2);
		a.vAdd(p2,p1);
	}
	
	for(int i=1;i<=m;++i){
		int keke,kaka,waiwai;
		scanf("%d%d%d",&keke,&kaka,&waiwai);
		a.solve(keke,kaka,waiwai);
	}
	
	return 0;
}
読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。