P4281 [AHOI2008] 紧急集合 / 聚会

[AHOI2008] 紧急集合 / 聚会 - 洛谷

树上三个点的路径上找一个点,聚集到一起,经过的边权值和最小

看样例以为是到比较3个点就可以了

写完发现询问2错误了

画图思考

看起来2是最完美的,不会走重复的路线

也可以知道2是3,6的lca,就想把每个lca都做一遍

O(1) dis(x,y)=dep[x]+dep[y]-2*dep[lca(x,y)

ans=dep[x]+dep[y]+dep[z]-2(dep[lca(x,y)]+dep[lca(x,z)+dep[lca(y,z)])

然后能a,就是很复杂的式子

后面发现3个点lca最多就2个

可以化简式子

p=lca(x,y)

ans=dep[x]+dep[y]+dep[z]-dep[p]-2*dep[lca(p,z)]

但是都是要枚举3个lca

于是思考如果存在2个lca是一样的就不应该在那边聚集,如果在那边聚集就会有至少2个人要走重复的路

因此如果有2个lca一样就选不一样的,3个一样就lca(x,y,z)

ans=dep[x]+dep[y]+dep[z]-dep[p]-2*dep[lca(x,y,z)]\

// Problem: P4281 [AHOI2008] 紧急集合 / 聚会
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P4281
// Memory Limit: 125 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
const int N=5e5+9;
int a[N];
int n,m;
//树剖
//1.转成线性部分
vector<int> e[N];
void add(int u,int v){
    e[u].push_back(v);
    e[v].push_back(u);
}
int fa[N],dep[N],sz[N],wc[N],dis[N];
void dfs1(int u,int f){//fa dep sz wc
    fa[u]=f;
    sz[u]=1;
    dep[u]=dep[f]+1;
    for(auto & i : e[u]){
        if(i==f){
            dis[u]=1;//边权给儿子
        }
        if(i!=f){
            dfs1(i,u);
            sz[u]+=sz[i];
            if(sz[i]>sz[wc[u]]){
                wc[u]=i;
            }
        }
    }
}
int dfn[N],rdfn[N],top[N],vistime;
void dfs2(int u,int Top){//dfn rdfn top
    dfn[u]=++vistime;
    a[vistime]=dis[u];//
    top[u]=Top;
    if(wc[u]){
        dfs2(wc[u],Top);
        for(auto & i : e[u]){
            if(i!=wc[u] && i!=fa[u]){
                dfs2(i,i);
            }
        }
    }
}
int lca(int u,int v){
	while(top[u]!=top[v]){
		if(dep[top[u]]<dep[top[v]]){
			swap(u,v);
		}
		u=fa[top[u]];
	}
	return dep[u]>dep[v]?v:u;
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
	cin>>n>>m;
	for(int i=1;i<=n-1;i++){
		int a,b;
		cin>>a>>b;
		add(a,b);
	}
	dfs1(1,0);
	dfs2(1,1);
	// t.build(1,1,n);
	for(int i=1;i<=m;i++){
		int x,y,z;
		cin>>x>>y>>z;
		int p1=lca(x,y),p2=lca(x,z),p3=lca(y,z);
		int p=0;
		int P=lca(p1,p2);
		if(p1==p2){
			p=p3;
			// cout<<p3<<" "<<(dep[x]+dep[y]+dep[z]-dep[p3]-2*dep[lca(p3,p1)])<<'\n';
			// continue;
		}
		if(p2==p3){
			p=p1;
			// cout<<p1<<" "<<(dep[x]+dep[y]+dep[z]-dep[p1]-2*dep[lca(p3,p1)])<<'\n';
			// continue;
		}
		if(p1==p3){
			p=p2;
			// cout<<p2<<" "<<(dep[x]+dep[y]+dep[z]-dep[p2]-2*dep[lca(p2,p1)])<<'\n';
		}
		//dis(x,y)=dep[x]+dep[y]-2*dep[lca(x,y)]
		cout<<p<<" "<<(dep[x]+dep[y]+dep[z]-dep[p]-2*dep[P])<<'\n';
	}
	return 0;
}

### AHOI2008 计算器问题解析 对于AHOI2008中的计算器问题,题目描述涉及一种特殊的计算器操作模式。该计算器支持两种基本运算:加法和乘法,并且可以执行逆向操作来撤销最近的一次计算。 #### 题目背景与目标 给定一系列的操作指令序列,每条指令可能是增加某个数值、将当前值翻倍或是回退至上一步的结果。程序需模拟这些命令的效果并最终输出指定时刻的状态值[^1]。 #### 数据结构的选择 为了高效处理上述类型的查询请求,在此场景下推荐采用栈(Stack)作为主要的数据存储机制。通过维护一个用于记录历史状态变化的栈表,可以在O(1)时间内完成入栈(push)/出栈(pop),从而满足快速响应的要求。 #### 关键算法逻辑 当遇到`ADD x`这样的正向修改时,只需简单地把新加入的数压入栈顶;而面对`MULTIPLY BY TWO`的情况,则应先保存现有总和再将其加倍后存入堆栈顶部。特别注意的是,“取消”动作意味着弹出最新一次变更前的状态恢复原状即可。 ```cpp #include <iostream> #include <stack> using namespace std; int main() { int n; cin >> n; long long current_value = 0LL; stack<long long> history; while (n--) { string command; cin >> command; if (command == "ADD") { int value_to_add; cin >> value_to_add; // Save the state before addition. history.push(current_value); current_value += value_to_add; } else if (command == "MULTIPLY_BY_TWO") { // Record pre-multiplication status and double it. history.push(current_value); current_value *= 2LL; } else { // UNDO operation if (!history.empty()) { current_value = history.top(); history.pop(); } } } cout << current_value << endl; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值