CF60C Mushroom Strife 解题报告
1 题目链接
https://codeforces.com/problemset/problem/60/C
2 题目整理
题目 :蘑菇冲突
题目描述
帕夏和阿基姆正在绘制一张地图——草坪是地图的顶点,连接草坪的道路是地图的边。他们决定用以下方式对每片草坪上的蘑菇数量进行编码:在两片草坪之间的每一条边上,他们写下两个数字,即这些草坪上蘑菇数量的最大公约数(GCD)和最小公倍数(LCM)。但有一天,帕夏和阿基姆就这些蘑菇发生了争执,并撕毁了地图。帕夏只剩下一部分,只有m条路。你的任务是帮助帕夏——利用他拥有的地图恢复每片草坪上蘑菇的数量。由于结果不一定是唯一的,请帮助帕夏恢复任何一种蘑菇,或报告这种蘑菇排列不存在。保证初始地图上的道路数量不少于1,且不超过 1 0 6 10^6 106。
输入格式
第一行包含两个数字n和m,这是我们知道的草坪和道路的数量。下面m组数据中每一组包含四个数字,即道路连接的草坪数量、这些草坪上蘑菇数量的最大公因数和最小公倍数。
可以保证的是,没有任何道路将草坪连接到自身,也没有两个草坪通过一条以上的道路连接。
输出格式
答案的第一行应是“YES”或“NO”,说明是否可能执行该安排。如果答案是“YES”,请在下一行打印相应草坪上蘑菇的数量。
样例输入1
1 0
样例输出1
YES
1
样例输入2
2 1
1 2 1 3
样例输出2
YES
1 3
样例输入3
3 2
3 2 1 2
3 1 1 10
样例输出3
YES
5 1 2
样例输入4
2 1
1 2 3 7
样例输出4
NO
数据范围
对于
100
%
100\%
100%的数据:
1
≤
n
≤
100
1 \le n \le 100
1≤n≤100
1
≤
m
≤
n
×
(
n
+
1
)
2
1 \le m \le \frac{n \times (n + 1)}{2}
1≤m≤2n×(n+1)
1
≤
G
C
D
,
L
C
M
≤
1
0
6
1 \le GCD, LCM \le 10^6
1≤GCD,LCM≤106
3 题意分析
3.1 题目大意
给定一个n个点,m条边的无向图,每个点上有一个数值,每条边上标记着它连接的两个点的GCD和LCM。请给出一种点上的数值的构造。若没有,输出 " N O " "NO" "NO",否则 “ Y E S ” “YES” “YES”加构造
3.2 样例分析
如上所述。
4 解法分析
先考虑数据范围,n小于100,所以题目应该可以用暴力或深搜来做。再分析题目。有公式: a × b = G C D ( a , b ) ∗ L C M ( a , b ) a \times b = GCD(a,b) * LCM(a,b) a×b=GCD(a,b)∗LCM(a,b),所以只要确定了a,就能推出b。类似的,只要确定一个连通快里的一个数,就能推出此连通块内的所有数。那么,我们可以先枚举其中一个数,再推出其他的数,看可不可行。这样,整道题就轻松了许多。
AC代码
ACCode #001
// From nvmdava
// Rating 2447
// reason : 思路清晰,代码简洁易懂
#include <bits/stdc++.h>
using namespace std;
struct Edge{
int to;
long long gcd, lcm;
Edge(int _u, int _gcd, int _lcm){
to = _u;
gcd = _gcd;
lcm = _lcm;
}
};
vector<Edge> to[105];
long long a[105];
void clr(int v){
a[v] = 0;
for(auto x : to[v])
if(a[x.to])
clr(x.to);
}
bool dfs(int v){
for(auto x : to[v]){
if(a[x.to]){
if(__gcd(a[v], a[x.to]) != x.gcd) return 0;
if(a[v] * a[x.to] != x.lcm * x.gcd) return 0;
continue;
}
a[x.to] = x.lcm * x.gcd / a[v];
if(a[x.to] <= 0 || a[x.to] > 1000000) return 0;
if(__gcd(a[v], a[x.to]) != x.gcd) return 0;
if(a[v] * a[x.to] != x.lcm * x.gcd) return 0;
if(!dfs(x.to)) return 0;
}
return 1;
}
int main(){
ios_base::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int n, m;
cin>>n>>m;
while(m--){
int v, u, g, l;
cin>>v>>u>>g>>l;
to[v].push_back(Edge(u, g, l));
to[u].push_back(Edge(v, g, l));
}
for(int i = 1; i <= n; i++){
if(a[i]) continue;
for(int j = 1; j <= 1000000; j++){
a[i] = j;
if(!dfs(i)) clr(i);
else break;
}
if(!a[i]){
cout<<"NO";
return 0;
}
}
cout<<"YES\n";
for(int i = 1; i <= n; i++)
cout<<a[i]<<' ';
}
ACCode #002
// From rng_58
// Rating 3074
// reason : 代码简洁明了清晰,
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <deque>
#include <queue>
#include <set>
#include <map>
#include <algorithm>
#include <functional>
#include <utility>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <cstdio>
using namespace std;
#define REP(i,n) for((i)=0;(i)<(int)(n);(i)++)
#define foreach(c,itr) for(__typeof((c).begin()) itr=(c).begin();itr!=(c).end();itr++)
typedef long long ll;
int N;
ll a[110][110],b[110][110]; // gcd, lcm
ll c[110];
ll gcd(ll x, ll y){
return x ? gcd(y%x,x) : y;
}
bool dfs(int p, ll x){
int i;
if(c[p] != 0 && c[p] != x) return false;
if(c[p] != 0) return true;
c[p] = x;
REP(i,N) if(a[p][i] > 0){
if(a[p][i] * b[p][i] % c[p] != 0) return false;
ll y = a[p][i] * b[p][i] / c[p];
if(gcd(c[p],y) != a[p][i]) return false;
if(!dfs(i,y)) return false;
}
return true;
}
void clear(int p){
int i;
if(c[p] == 0) return;
c[p] = 0;
REP(i,N) if(a[p][i] > 0) clear(i);
}
int main(void){
int i,j,E,x,y,_a,_b;
scanf("%d%d",&N,&E);
REP(i,E){
scanf("%d%d%d%d",&x,&y,&_a,&_b); x--; y--;
a[x][y] = a[y][x] = _a; b[x][y] = b[y][x] = _b;
}
bool failed = false;
REP(i,N) if(c[i] == 0){
REP(j,N) if(b[i][j] != 0) break;
if(j == N) {c[i] = 1; continue;}
bool flag = false;
for(x=1;x*x<=b[i][j];x++) if(b[i][j]%x == 0){
if(dfs(i,x)) {flag = true; break;} clear(i);
if(dfs(i,b[i][j]/x)) {flag = true; break;} clear(i);
}
if(!flag) {failed = true; break;}
}
if(failed){
cout << "NO" << endl;
} else {
cout << "YES" << endl;
REP(i,N){
cout << c[i];
if(i == N-1) cout << endl; else cout << ' ';
}
}
return 0;
}
ACCode #003
// From KrK
// Rating 2787
// reason : 思路清晰,代码简单易懂,可读性强
#include <iostream>
#include <vector>
using namespace std;
const int Maxn = 101;
const int Inf = 1000001;
int n, m, num[Maxn];
vector <int> neigh[Maxn], g[Maxn], l[Maxn];
bool del[Maxn], taken[Maxn], stop;
int gcd(int x, int y)
{
while (x != 0) {
int tmp = y % x;
y = x;
x = tmp;
}
return y;
}
void Search(int v)
{
int i;
for (i = 0; i < neigh[v].size() && !stop; i++)
if (num[v] % g[v][i] || l[v][i] % num[v]) stop = true;
else {
int newnum = l[v][i] / num[v] * g[v][i];
if (gcd(newnum, num[v]) != g[v][i] ||
num[neigh[v][i]] && num[neigh[v][i]] != newnum) stop = true;
else if (num[neigh[v][i]]) continue;
else {
num[neigh[v][i]] = newnum;
Search(neigh[v][i]);
}
}
}
void DFS(int v)
{
if (del[v]) return;
del[v] = true;
for (int i = 0; i < neigh[v].size(); i++)
DFS(neigh[v][i]);
}
void SwitchBack(int v)
{
if (num[v] == 0) return;
num[v] = 0;
for (int i = 0; i < neigh[v].size(); i++)
SwitchBack(neigh[v][i]);
}
int main()
{
int a, b, c, d;
cin >> n >> m;
for (int i = 0; i < m; i++) {
cin >> a >> b >> c >> d;
neigh[a].push_back(b); g[a].push_back(c); l[a].push_back(d);
neigh[b].push_back(a); g[b].push_back(c); l[b].push_back(d);
}
int i;
for (i = 1; i <= n; i++) if (!del[i]) {
int j;
for (j = 1; j < Inf; j++) {
stop = false;
num[i] = j;
Search(i);
if (!stop) break;
SwitchBack(i);
}
if (j == Inf) break;
DFS(i);
}
if (i <= n) cout << "NO\n";
else {
cout << "YES\n";
for (int i = 1; i <= n; i++) {
if (i > 1) cout << " ";
cout << num[i];
}
cout << endl;
}
return 0;
}