持续创作,加速成长!这是我参与「掘金日新计划 · 1 月更文挑战」的第14天,点击查看活动详情
题目大意
一个齿轮系统是蒸汽朋克的,当且仅当这个齿轮系统可以顺利运转。即对于任意两个啮合的齿轮,设它们的半径和角速度分别为 和 ,满足 。
对于正整数 ,分别表示齿轮数量,啮合关系数量和已知角速度的齿轮数量。分别给出如下信息:
- 个齿轮的半径 。
- 对整数 ,表示齿轮 和齿轮 啮合。
- 组整数对 ,表示齿轮 的角速度为 ,保证 互不相同。
试求所有齿轮的角速度。若无解,输出 It is not steampunk!,若解不唯一,输出 oo
思路
容易发现一个连通块中如果有一个齿轮的角速度已知,我们就可以推出整个连通块中所有齿轮的角速度。对于有已知角速度 的齿轮 ,遍历与其啮合的所有齿轮 :
- 若齿轮 v 角速度没有确定,则用 来更新它,
- 若齿轮 v 有 ,则判断二者是否满足题目中的关系式 。如果不能满足关系式,即两个齿轮冲突,我们输出
It is not steampunk!。
当把所有可以通过已知的 w 推出的信息通过搜索全部更新完毕后,我自信的写出解不唯一的判断条件——如果访问过的齿轮数量不等于n,则输出 oo。
于是 Wa 了 15 发(
仔细想了一想我的判断条件,发现因为 全为正数,存在啮合关系的两个齿轮的 只可能全为 0 或者一正一负。比如有 a,b,c 三个齿轮,a 和 b 啮合了,a 和 c 啮合了,b 和 c 啮合了,虽然三者都没有指定角速度,但是它们的角速度必然都为 0。推广一下,如果一个连通块中存在奇环,那么其中所有齿轮的角速度其实也已经确定。即解不唯一的条件其实是“存在没有访问过的不在奇环里的齿轮”。综上,我们现在面临的问题变成了怎么才能判断一个连通块里有奇环……
强连通分量黑白染色?当然不。
易知在不含奇环且所有齿轮都不确定角速度的连通块中,任取一个齿轮给它赋值任意的 ,都可以通过调整其他点的 使得该连通块不存在冲突。
遍历没有在搜索中确定角速度的齿轮,让它随便等于一个非零数字,进行一次搜索:
- 如果没产生冲突,我们已经求出了两种合法解,可以理直气壮地输出
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;
}