P4079 [SDOI2016]齿轮 link
思路:
嗯这题据说double会爆精度,暴力乘会爆longlong。。
方法是用dfs,正常算比例是乘上vt除以vf(vt,vf定义见代码),
然后因为要取模所以除法变成算逆元
然后如果是个无环图那就应该没问题,
如果是个有环图(一个点能被dfs到不止一次)那就特判一下,
看两次遍历的结果是否一致,不一致说明不行
#include <cstdio>
#include <cstring>
#include <vector>
#define ll long long
using namespace std;
const int maxn = 3e3 + 5;
const int maxm = 2e4 + 5;
struct Edge
{
int from, to;
ll vt, vf;
Edge() {}
Edge(int f, int t, ll _vt, ll _vf) {from = f, to = t, vt = _vt, vf = _vf;}
}e[maxm];
struct Edge1
{
int from, to;
ll c;
Edge1() {}
Edge1(int f, int t, ll _c) {from = f, to = t, c = _c;}
};
vector <Edge> edge[maxn];
int n, m;
ll node[maxn];
bool vis[maxn];
//本题有负数,取模需要对其加到正后再取,C++负数取模比较诡异
inline ll mod(ll a, ll p)
{
return ((a%p) + p) % p;
}
void Init()
{
for (int i = 1; i <= n; i++)
edge[i].clear();
memset(vis, 0, sizeof(vis));
memset(node, 0, sizeof(node));
}
//快速幂,用于算逆元
ll getn(ll a, ll p)
{
ll ans = 1, b = mod(a, p);
ll z = p - 2;
while (z != 0)
{
if (z & 1)
{
ans *= b;
ans %= p;
}
b *= b;
b %= p;
z >>= 1;
}
return ans;
};
bool dfs(int u, ll z, ll p)
{
vis[u] = 1, node[u] = z;
bool ands = 1;
bool gg[maxn] = { 0 };
int sz = edge[u].size();
for (int i = 0; i < sz; i++)
{
int v = edge[u][i].to;
//已经遍历的情况,并且两次遍历结果一致,说明该点暂时可行
if (vis[v] && node[v] == (z*edge[u][i].vf % p)*edge[u][i].vt % p) gg[i] = 1;
//如果没被遍历过就往下遍历
else if (!vis[v])
gg[i] = dfs(v, (z*edge[u][i].vf % p)*edge[u][i].vt % p, p);
}
//必须所有子结点点都可行才可行
for (int i = 0; i < sz; i++)
if (!gg[i])
ands = 0;
return ands;
}
bool work_work(ll p)
{
Init();
for (int i = 1; i <= m; i++)
{
edge[e[i].from].push_back(Edge(e[i].from, e[i].to, getn(e[i].vf, p), mod(e[i].vt, p)));
edge[e[i].to].push_back(Edge(e[i].to, e[i].from, getn(e[i].vt, p), mod(e[i].vf, p)));
}
int flag = 1;
//图不一定连通,需要判定每一个连通的部分是否可行
for (int i = 1; i <= n; i++)
if (!vis[i])
flag = ((dfs(i, 1, p) == true) ? flag : false);
return flag;
}
void work(int i)
{
memset(e, 0, sizeof(e));
scanf("%d%d", &n, &m);
for (int a = 1; a <= m; a++)
scanf("%d%d%lld%lld", &e[a].from, &e[a].to, &e[a].vf, &e[a].vt);
bool ans = (work_work(1e9 + 7) && work_work(1e9 + 9));
printf("Case #%d: ", i);
if (ans) printf("Yes\n");
else printf("No\n");
}
int main()
{
int t;
scanf("%d", &t);
for (int i = 1; i <= t; i++) work(i);
return 0;
}