【HBCPC2021河北省大学生程序设计竞赛】F - 蒸汽朋克

381 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 1 月更文挑战」的第14天,点击查看活动详情

题目大意

一个齿轮系统是蒸汽朋克的,当且仅当这个齿轮系统可以顺利运转。即对于任意两个啮合的齿轮,设它们的半径和角速度分别为 r1,ω1r_1,ω_1 和 r2,ω2r_2,ω_2,满足 r1×ω1+r2×ω2=0r_1\times ω_1+r_2\times ω_2=0

对于正整数 n,m,k(1n,m105,kn)n,m,k(1≤n,m≤10^5,k≤n),分别表示齿轮数量,啮合关系数量和已知角速度的齿轮数量。分别给出如下信息:

  1. nn 个齿轮的半径 r1,r2,,rn(1ri103)r_1,r_2,⋯,r_n(1≤r_i≤10^3)
  2. mm 对整数 u,v(1u,vn)u,v(1≤u,v≤n),表示齿轮 uu 和齿轮 vv 啮合。
  3. kk 组整数对 p,ω(1pn,109ω109)p,ω(1≤p≤n,−10^9≤ω≤10^9) ,表示齿轮 pp 的角速度为 ωω,保证 pp 互不相同。

试求所有齿轮的角速度。若无解,输出 It is not steampunk!,若解不唯一,输出 oo

思路

容易发现一个连通块中如果有一个齿轮的角速度已知,我们就可以推出整个连通块中所有齿轮的角速度。对于有已知角速度 ωu\omega_u 的齿轮 uu,遍历与其啮合的所有齿轮 vv

  • 若齿轮 v 角速度没有确定,则用 ru×ωurv-\frac{r_u\times \omega_u}{r_v} 来更新它,
  • 若齿轮 v 有 wvw_v,则判断二者是否满足题目中的关系式 r1×ω1+r2×ω2=0r_1\times \omega_1+r_2\times \omega_2=0。如果不能满足关系式,即两个齿轮冲突,我们输出 It is not steampunk!

当把所有可以通过已知的 w 推出的信息通过搜索全部更新完毕后,我自信的写出解不唯一的判断条件——如果访问过的齿轮数量不等于n,则输出 oo

于是 Wa 了 15 发(

仔细想了一想我的判断条件,发现因为 rr 全为正数,存在啮合关系的两个齿轮的 ww 只可能全为 0 或者一正一负。比如有 a,b,c 三个齿轮,a 和 b 啮合了,a 和 c 啮合了,b 和 c 啮合了,虽然三者都没有指定角速度,但是它们的角速度必然都为 0。推广一下,如果一个连通块中存在奇环,那么其中所有齿轮的角速度其实也已经确定。即解不唯一的条件其实是“存在没有访问过的不在奇环里的齿轮”。综上,我们现在面临的问题变成了怎么才能判断一个连通块里有奇环……

强连通分量黑白染色?当然不。

易知在不含奇环且所有齿轮都不确定角速度的连通块中,任取一个齿轮给它赋值任意的 ω0\omega_0,都可以通过调整其他点的 ω\omega使得该连通块不存在冲突。

遍历没有在搜索中确定角速度的齿轮,让它随便等于一个非零数字,进行一次搜索:

  • 如果没产生冲突,我们已经求出了两种合法解,可以理直气壮地输出 oo 了。
  • 如果产生了冲突,就说明该齿轮的角速度一定等于 0,可以确定它所在的连通块里所有的齿轮的角速度均为 0。

如果最终既不 It is not steampunk! 也不 oo,则输出我们求出的每个齿轮的角速度。

代码

#include <stdio.h>
#include <algorithm>
#include <vector>
#include <math.h>
using namespace std;
const int N=100001;
vector<int> e[N];
int n,m,k;
double r[N],w[N],qwq=0.000001,val,ans[N];
int d[N],vis[N],h,t,cnt;
int bfs()
{
    int flag=1;
	while (h!=t)
	{
		cnt++;
		int u=d[++h]; 
		for (auto v:e[u])
		{
			val=-r[u]*w[u]/r[v];
			if (!vis[v])
			{
				w[v]=val;
				vis[v]=1;
				d[++t]=v;
			}
			if (fabs(r[u]*w[u]+r[v]*w[v])>qwq) flag=0;
		}
	}
	return flag;
}
int main()
{
	scanf("%d%d%d",&n,&m,&k);
	for (int i=1;i<=n;++i) scanf("%lf",&r[i]);
	for (int i=1,x,y;i<=m;++i) scanf("%d%d",&x,&y),e[x].push_back(y),e[y].push_back(x);
	for (int i=1,x;i<=k;++i)
	{
		scanf("%d",&x);
		d[++t]=x;
		scanf("%lf",&w[x]);
		vis[x]=1;
	}	
	if (!bfs()) return printf("It is not steampunk!"),0;
	for (int i=1;i<=n;++i) ans[i]=w[i];
	if (cnt!=n)
	{
		for (int i=1;i<=n;++i)
			if (!vis[i])
			{
				vis[i]=1;
				d[++t]=i;
				w[i]=123;
                if (bfs()) return printf("oo"),0;
			}
	}
	printf("Steampunk!\n");
	for (int i=1;i<n;++i) printf("%.4f ",ans[i]);
	printf("%.4f",ans[n]);
	return 0;
}