把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

PA2014 Fiolki

题目链接

题目解析

神奇题目,洛谷评分居然是普及+,果然还是我太菜了吗qwq

题意我也看了好一会儿,我以为它是一个类似于化合反应的东西,把试剂a倒入试剂b之后,药水就全部变成试剂b了,而质量为两者的和(质量守恒定律)。

但实际上题目的意思只是把这两种药水混合在一起,是物理变化,只有生成的那个沉淀是化学变化。

首先可以想到一个O(mk)的大暴力,但是数据范围显然过不去。

考虑到重要的是这些沉淀们各自的顺序,因为顺序不同,发生反应时反应物的已消耗量可能不同,也就是反应物的量可能不同,产物的量可能不同。

所以重点在于判断各个沉淀的顺序。

神奇的做法来喏,就是其实把两个反应物向生成物连一条边(我刚开始想的是直接a>b连边了,没有想到把b自己拆成反应物和生成物,然后没有做出来),那么就形成了一个二叉树森林(不一定联通),叶子结点都是原来的试剂,如果两种试剂会反应,那么它们会在lca的位置相遇,然后反应生成沉淀,生成的沉淀质量为2倍较小质量数,然后各自减去这个转化量。

显然lca深度比较大沉淀先发生,于是我们就找出了这些沉淀的顺序。注意lca深度相同时,要按照给出顺序排,题目说了按照反应优先顺序给出这k对可以反应的物质。

用倍增求lcs,时间复杂度应该为O(k×log(n+m))


►Code View

#include<cstdio>
#include<algorithm>
#include<vector>
#include<queue>
#include<cstring>
using namespace std;
#define N 200005
#define M 200005
#define K 500005
#define INF 0x3f3f3f3f
#define LL long long
int rd()
{
	int x=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
	return f*x;
}
int n,m,k;
int g[N];
vector<int>G[N+M];
int pos[N];//当前试剂i的结点编号 
int f[N+M][25],dep[N+M];
struct node{
	int a,b,cd,id;
}q[K];
int cnt;//有效沉淀反应个数 
LL ans;
void add(int u,int v)
{
	G[v].push_back(u);
}
bool cmp(node p,node q)
{
	if(p.cd==q.cd) return p.id<q.id;//注意k对试剂按照反应优先顺序给出
	//我刚开始以为同一层的顺序可以随便 实际上不行 因为试剂有量的多少 
	return p.cd>q.cd;
}
void dfs(int u)
{
	for(int i=1;i<=20;i++)
		f[u][i]=f[f[u][i-1]][i-1];
	for(int i=0;i<G[u].size();i++)
	{
		int v=G[u][i];
		if(v==f[u][0]) continue;
		f[v][0]=u;
		dep[v]=dep[u]+1;
		dfs(v);
	}
}
int LCA(int u,int v)
{
	int tmp;
	if(dep[u]<dep[v]) tmp=u,u=v,v=tmp;
	for(int j=20;j>=0;j--)
		if(dep[f[u][j]]>=dep[v])
			u=f[u][j];
	if(u==v) return u;
	for(int j=20;j>=0;j--)
		if(f[u][j]!=f[v][j])
			u=f[u][j],v=f[v][j];
	return f[u][0];
}
int main()
{
	n=rd(),m=rd(),k=rd();
	for(int i=1;i<=n;i++)
	{
		pos[i]=i;
		g[i]=rd();
	}
	for(int i=1;i<=m;i++)
	{
		int a=rd(),b=rd();
		add(pos[a],n+i);
		add(pos[b],n+i);
		pos[b]=n+i;
	}
	for(int i=n+m;i>=1;i--)
		if(f[i][0]==0)//可能不连通 即二叉树森林 
			dfs(i);
	for(int i=1;i<=k;i++)
	{
		int a=rd(),b=rd(),lca=LCA(a,b);
		if(!lca) continue;//这对沉淀不会出现
		node tmp; tmp.a=a,tmp.b=b,tmp.cd=dep[lca],tmp.id=i;
		q[++cnt]=tmp;
	}
	sort(q+1,q+cnt+1,cmp);
	for(int i=1;i<=cnt;i++)
	{
		int del=min(g[q[i].a],g[q[i].b]);
		g[q[i].a]-=del,g[q[i].b]-=del;
		ans=ans+2ll*del;
	}
	printf("%lld\n",ans);
	return 0;
}
posted @   Starlight_Glimmer  阅读(59)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
浏览器标题切换
浏览器标题切换end
点击右上角即可分享
微信分享提示