EOJ3652 乘法还原
题目链接
https://acm.ecnu.edu.cn/problem/3652/
题目大意
两个序列,分别是 $\{a_i\}$ 和 $\{b_i\}$,随后用这两个序列构造了一个表达式 $(x_{a_1} + … + x_{a_m})(x_{b_1} + … + x_{b_n})$。告诉你这个表达式的展开式,让你还原出这两个序列。
题目分析
对于给出的展开式中,如果有两项相同的话,说明 $a$ 和 $b$ 中都有这一项,可以直接将其加入 $a$ 和 $b$ 中。对于其他的,可使用 $DFS$ 对其进行染色,任何一个连边的都不属于同样的集合。注意有可能只有一边有未出现的项,需要单独处理。
代码
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <vector>
#include <map>
using namespace std;
const int maxn = 1e5 + 5;
int n;
vector <int> a;
vector <int> b;
vector <int> ansa;
vector <int> ansb;
map <int, bool> m;
map <int, vector <int> > e;
map <int, bool> v;
int read(){
int x;
scanf("%d", &x);
return x;
}
bool check(){
if(ansa.size() != ansb.size()){
return ansa.size() > ansb.size();
}else{
for(int i=0;i<ansa.size();i++){
if(ansa[i] != ansb[i]){
return ansa[i] > ansb[i];
}
}
}
return false;
}
int addedge(int x, int y){
e[x].push_back(y);
e[y].push_back(x);
return 0;
}
int dfs(int x, int d){
int i, j;
int y;
v[x] = true;
if(d == 0){
ansa.push_back(x);
}else{
ansb.push_back(x);
}
for(i=0;i<e[x].size();i++){
y = e[x][i];
if(!v[y]){
dfs(y, !d);
}
}
return 0;
}
int main(){
int i, j;
int x, y;
int root = 0;
int cnt = 0;
n = read();
for(i=1;i<=n;i++){
x = read();
y = read();
if(x == y){
ansa.push_back(x);
ansb.push_back(y);
m[x] = true;
cnt++;
}else{
a.push_back(x);
b.push_back(y);
}
}
for(i=0;i<a.size();i++){
x = a[i];
y = b[i];
if(m[x] or m[y]){
continue;
}else{
addedge(x, y);
root = x;
}
}
if(root)dfs(root, 0);
if(ansa.size() == cnt){
for(i=0;i<a.size();i++){
x = a[i];
y = b[i];
if(!m[x]){
ansb.push_back(x);
m[x] = true;
}else if(!m[y]){
ansb.push_back(y);
m[y] = true;
}
}
}
sort(ansa.begin(), ansa.end());
sort(ansb.begin(), ansb.end());
if(check()){
swap(ansa, ansb);
}
printf("%d %d\n", ansa.size(), ansb.size());
for(i=0;i<ansa.size();i++){
printf("%d ", ansa[i]);
}
printf("\n");
for(i=0;i<ansb.size();i++){
printf("%d ", ansb[i]);
}
printf("\n");
return 0;
}