LOJ3057
updated 11.7 发现在UOJ上TLE。改进了代码实现。 生成树是可以直接做的,减少了常数,这样每一颗树最多连一条自环,加一个标记即可。
SOL
30pts
从构造路径的角度思考,难以入手。总点数能实现
O
(
n
2
)
O(n^2)
O(n2)算法,转换思路,从中间向两边接点,构造回文串。
设
f
[
i
]
[
j
]
f[i][j]
f[i][j]表示
i
,
j
i,j
i,j点对能否作为回文路径的两端。故枚举
i
,
j
i,j
i,j的边尝试转移。
不难发现这样做总复杂度是
O
(
m
2
)
O(m^2)
O(m2)(每一对"边"最多被拿出两次)
100pts
优化建图,目标是将边数减少到
O
(
n
)
O(n)
O(n)级别。
先给出做法,再尝试证明。
做法:
我们把边分成两种,一种是两端点的颜色相同,另一种是不同。
对于前一种边,会形成若干个连通块。对于每一个连通块,如果是二分图,就做一颗生成树取代原连通块的边;如果不是就再随便找一个点加一个自环。
对于后一种边形成的连通块,由于颜色已经染好了,一定是一个二分图,做一颗生成树替代。
(前一种会形成多个互相独立的连通块,后一个连接各个连通块。)
证明:
我们要让原来的合法路径依然合法,且不会产生新的合法路径。
首先对于一条合法的路径,我们可以从中心划分成若干段,为
0
/
1
0/1
0/1交替段,全是
0
/
1
0/1
0/1段两种。更改原图后,我们只需要让关于中点相对称的两端依然能匹配(设为
A
A
A,
B
B
B两段)。
原来路径依然合法。
1.具体的长度不用考虑,只需要考虑段长的奇偶性。
由于点边可以重复经过,若
A
A
A段短了,可以重复走点走回来增加长度。但重复走点不会改变奇偶性。
2.对于一个同色连通块,只要两点能连通,且路径奇偶性不变即可
故如果有环,二分图中不同路径奇偶性不变,但是非二分图奇偶性要改变,多加一个自环就可以实现。
3.对于一个不同色连通块同理。
4.同色非二分图连通块中的任意一个点都可加自环,且可加任意多个(至少一个)
由2显然。 注意若加自环的点刚好也在另一个不同色连通块
S
S
S中,不会对
S
S
S产生影响。因为走两遍该点就不属于不同色段了。
原来不合法路径依然不合法
由上述证明,该建图方式利用的合法路径奇偶性的特点。 若原不合法路径奇偶性不合法,建图后奇偶性依然不合法。
CODE
#include<bits/stdc++.h>
using namespace std
;
#define sf scanf
#define ri register int
#define in red()
#define gc getchar()
#define cs const
#define ll long long
inline int red(){
int num
=0,f
=1;char c
=gc
;
for(;!isdigit(c
);c
=gc
)if(c
=='-')f
=-1;
for(;isdigit(c
);c
=gc
)num
=(num
<<1)+(num
<<3)+(c
^48);
return num
*f
;
}
cs
int N
=5e3+10,M
=5e5+10;
int n
,m
,Q
;
bool f
[N
][N
],col
[N
];
vector
<int>g
[N
];
struct edge
{
int u
,v
;
}e
[M
];
struct BCJ
{
int fa
[N
],d
[N
],ins
[N
];
inline int find(int u
){
if(!fa
[u
])return u
;
int t
=fa
[u
];
fa
[u
]=find(fa
[u
]);
d
[u
]^=d
[t
];
return fa
[u
];
}
inline void merge(int x
,int y
,int col
){
int fx
=find(x
),fy
=find(y
);
if(fx
^fy
){
d
[fx
]=d
[x
]^d
[y
]^1;
fa
[fx
]=fy
;
g
[x
].push_back(y
);
g
[y
].push_back(x
);
ins
[fy
]|=ins
[fx
];
}
else{
if(d
[x
]^d
[y
]^1)ins
[fx
]=1;
}
}
}G
[2];
queue
<edge
> q
;
inline void solve(){
for(ri i
=1;i
<=n
;++i
)f
[i
][i
]=1,q
.push((edge
){i
,i
});
for(ri u
=1;u
<=n
;++u
){
for(ri i
=g
[u
].size()-1;i
>=0;--i
){
int v
=g
[u
][i
];
if(!f
[u
][v
]&&col
[u
]==col
[v
]){
f
[u
][v
]=f
[v
][u
]=1;
q
.push((edge
){u
,v
});
}
}
}
while(!q
.empty()){
edge t
=q
.front();q
.pop();
int x
=t
.u
,y
=t
.v
;
for(ri i
=g
[x
].size()-1;i
>=0;--i
){
for(ri j
=g
[y
].size()-1;j
>=0;--j
){
int vx
=g
[x
][i
],vy
=g
[y
][j
];
if(col
[vx
]==col
[vy
]&&!f
[vx
][vy
]){
f
[vx
][vy
]=f
[vy
][vx
]=1;
q
.push((edge
){vx
,vy
});
}
}
}
}
}
char s
[N
];
signed main(){
n
=in
;m
=in
;Q
=in
;
sf("%s",s
+1);
for(ri i
=1;i
<=n
;++i
){
col
[i
]=s
[i
]^48;
}
for(ri i
=1;i
<=m
;++i
){
e
[i
]=(edge
){in
,in
};
}
for(ri i
=1;i
<=m
;++i
){
G
[col
[e
[i
].u
]^col
[e
[i
].v
]].merge(e
[i
].u
,e
[i
].v
,col
[e
[i
].u
]^col
[e
[i
].v
]);
}
for(ri i
=1;i
<=n
;++i
)if(G
[0].find(i
)==i
&&G
[0].ins
[i
])g
[i
].push_back(i
);
solve();
while(Q
--){
int x
=in
,y
=in
;
if(f
[x
][y
])cout
<<"YES\n";
else cout
<<"NO\n";
}
return 0;
}