AtCoder Regular Contest 095
一个图上的行交换与列交换是独立的,并且同一个行或同一个列上的数不会因为变换而出现在不同行或不同列(自己模拟一下就知道了...)。
一个比较朴素的想法就是先搜出行交换再找列交换,直接搜很明显会T飞,根据题目要求的对称性质搜对称方式可以将原先 O(12!) 的爆搜复杂度优化到10000多。
比如说 , 对于以下数据 , 我们令 $ match[i] $ 为 $ S_{i} $ 在一种对称方式中关于 $ (n + 1) / 2 $ 对称的对称行(我们先假设再验证)。
3 7
atcoder
regular
contest
match[1] = 2 , match[2] = 1 , match[3] = 3
表示1 和 2 对称 , 3 和自己对称。
得到行的对称方式以后再check列的对称方式。
行的对称方式已经给我们决定了列的对称方式 , 即此时对于两个列 $ i j $ 我们说他们相互对称当且仅当列中每一行 $ k $ 满足 $ S[k][i] = S[match[k]][j] $
当列数为奇数时还需要特判一下唯一一个不能匹配的列能否按照 $ S[k][i] = S[match[k]][j] $ 对称
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<map>
#include<queue>
#include<set>
#include<vector>
#include<cstdio>
using namespace std;
typedef long long ll;
const int N = 14 , Mod = 998244353 , G = 3 , INF = 0x3f3f3f3f;
double PI = acos(-1);
struct Virt{
double x , y;
Virt(double _x = 0.0 , double _y = 0.0):x(_x) , y(_y){}
};
Virt operator + (Virt x , Virt y){return Virt(x.x + y.x , x.y + y.y);}
Virt operator - (Virt x , Virt y){return Virt(x.x - y.x , x.y - y.y);}
Virt operator * (Virt x , Virt y){return Virt(x.x * y.x - x.y * y.y , x.x * y.y + x.y * y.x);}
int read(){
int x = 0 , f = 1; char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') f = -1 ; ch = getchar();}
while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0'; ch = getchar();}
return x * f;
}
int H , W;
int marked[N] , match[N];
char s[N][N]; bool book[N];
bool check(){
memset(marked , 0 , sizeof(marked));
for(int i = 1 ; i <= W ; i++){
if(marked[i]) continue;
for(int j = 1 ; j <= W ; j++){
if(marked[j] || (i == j)) continue;
bool flag = true;
for(int k = 1 ; k <= H ; k++)
if(s[k][i] != s[match[k]][j]){
flag = false;
break;
}
if(flag){
marked[i] = marked[j] = 1;
break;
}
}
}
int pos = 0 , cnt = 0;
for(int i = 1 ; i <= W ; i++)
if(!marked[i]) cnt++ , pos = i;
if(cnt >= 2 || (cnt && (W % 2 == 0))) return false;
if(cnt == 1)
for(int i = 1 ; i <= H ; i++)
if(s[i][pos] != s[match[i]][pos])
return false;
return true;
}
bool ans;
void dfs(int cnt , bool odd){
if(cnt == ((H + 1) >> 1) + 1){
if(check())
ans = true;
return;
}
if(ans) return;
for(int i = cnt ; i <= H ; i++){
if(!match[i]){
for(int j = i + 1 ; j <= H ; j++)
if(!match[j]){
match[i] = j; match[j] = i;
dfs(cnt + 1 , odd);
match[i] = match[j] = 0;
}
if(odd){
match[i] = i;
dfs(cnt + 1 , 0);
match[i] = 0;
}
}
}
}
int main(){
H = read() , W = read();
for(int i = 1 ; i <= H ; i++)
scanf("%s",s[i] + 1);
if(H & 1) dfs(1 , 1);
else dfs(1 , 0);
if(ans) printf("YES\n");
else printf("NO\n");
return 0;
}
(眼疾退役鹰文废柴选手看错半天题面ORZ)
根据题面所给的偏序关系和样例的提示,可以知道一棵树是可以被一个排列映射当且仅当这棵树是一个毛毛虫
所以我们先dfs找一下树的直径,再判一下这棵树是否为毛毛虫,然后根据套路在这个直径上做贪心就行了。
不过对于直径我们要分类讨论1的位置,直径的第一个节点和最后一个节点分别求一次即可。
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<map>
#include<queue>
#include<set>
#include<vector>
#include<cstdio>
using namespace std;
typedef long long ll;
const int N = 1e5 + 5 , Mod = 998244353 , G = 3 , INF = 0x3f3f3f3f;
double PI = acos(-1);
struct Virt{
double x , y;
Virt(double _x = 0.0 , double _y = 0.0):x(_x) , y(_y){}
};
Virt operator + (Virt x , Virt y){return Virt(x.x + y.x , x.y + y.y);}
Virt operator - (Virt x , Virt y){return Virt(x.x - y.x , x.y - y.y);}
Virt operator * (Virt x , Virt y){return Virt(x.x * y.x - x.y * y.y , x.x * y.y + x.y * y.x);}
int read(){
int x = 0 , f = 1; char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') f = -1 ; ch = getchar();}
while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0'; ch = getchar();}
return x * f;
}
struct edge{
int pos , nx;
}e[N << 1];
int nxt[N] , pre[N] , hd , tl , num , head[N] , dep[N][2] , n;
int length , s[2][N] , cnt[N] , d[N];
void add(int u , int v){
e[++num].pos = v; e[num].nx = head[u]; head[u] = num;
e[++num].pos = u; e[num].nx = head[v]; head[v] = num;
}
void get_deep(int now , int fa){
int fir = 0 , sec = 0;
dep[now][0] = 1 , dep[now][1] = now;
for(int i = head[now] ; i ; i = e[i].nx){
if(e[i].pos == fa) continue;
get_deep(e[i].pos , now);
if(dep[e[i].pos][0] + 1 > dep[now][0]){
dep[now][0] = dep[e[i].pos][0] + 1;
dep[now][1] = dep[e[i].pos][1];
}
if(dep[e[i].pos][0] > dep[fir][0]) sec = fir , fir = e[i].pos;
else if(dep[e[i].pos][0] > dep[sec][0]) sec = e[i].pos;
}
if(dep[fir][0] + dep[sec][0] + 1 > length){
hd = dep[fir][1];
tl = dep[sec][1];
length = dep[fir][0] + dep[sec][0] + 1;
}
if(dep[now][0] > length){
hd = now;
tl = dep[now][1];
length = dep[now][0];
}
}
int comp(int a[] , int b[]){
for(int i = 1 ; i <= n ; i++){
if(a[i] == b[i]) continue;
else return a[i] < b[i] ? 0 : 1;
}
return 0;
}
bool dfs(int now , int fa){
if(now == tl) return true;
for(int i = head[now] ; i ; i = e[i].nx){
if(e[i].pos == fa) continue;
if(dfs(e[i].pos , now)){
pre[e[i].pos] = now , nxt[now] = e[i].pos;
cnt[now] = d[now] - 2;
return true;
}
}
return false;
}
int main(){
int u , v;
n = read();
for(int i = 1 ; i < n ; i++){
u = read() , v = read(); add(u , v);
d[u]++; d[v]++;
}
get_deep(1 , 0);
dfs(hd , 0);
cnt[hd]++;
bool flag = true;
for(int i = 1 ; i <= n ; i++){
if(nxt[i] == 0){
for(int j = head[i] ; j ; j = e[j].nx)
if(nxt[e[j].pos] == 0) flag = false;
}
}
if(!flag) return 0 * printf("-1\n");
int cur = 1 , now = 1 , temp;
int i = 0; nxt[0] = hd;
do{
i = nxt[i];
temp = cur;
for(int j = 1 ; j <= cnt[i] ; j++){
cur++;
s[0][now++] = cur;
}
s[0][now++] = temp; cur++;
}while(i != tl);
cur = now = 1 , i = 0 , pre[0] = tl;
do{
i = pre[i];
temp = cur;
for(int j = 1 ; j <= cnt[i] ; j++){
cur++;
s[1][now++] = cur;
}
s[1][now++] = temp; cur++;
}while(i != hd);
int ans = comp(s[0] , s[1]);
for(int i = 1 ; i <= n ; i++) printf("%d " , s[ans][i]);
return 0;
}