[题解]T723953 「LDOI R2」平方数 Square
题目:# P17765 「LDOI R2」平方数 / Square
题目描述
有 $n$ 个给定的二元组 $(a_i, b_i)$。
称一个长度为 $m\ (m>1)$ 的序列 $p_1, p_2, \dots, p_m\ (1\le p_i\le n)$ 是好的序列,当且仅当它满足以下条件:
$p_i$ 互不相同。
$a_{p_1}$ 是完全平方数。
$b_{p_m}$ 是完全平方数。
对于所有的 $1 \le i < m$,$a_{p_{i+1}} b_{p_i}$ 是完全平方数。
求所有好的序列中 $m$ 的最小值,若不存在好的序列输出 $-1$。
输入格式
本题有多组测试用例。
第一行输入一个正整数 $T$ 表示测试用例组数,接下来对于每一组测试用例:
第一行输入一个正整数 $n$。
接下来 $n$ 行,每行输入两个正整数,第 $i+1$ 行的两个数表示 $a_i, b_i$。
输出格式
对于每组测试用例:
若存在好的序列,则输出所有好的序列中,$m$ 的最小值,若不存在好的序列输出 $-1$。
输入输出样例 #1
输入 #1
3 5 4 5 20 9 8 9 16 3 12 2 1 1 1 2 1 1 1 1输出 #1
2 -1 2输入输出样例 #2
输入 #2
1 9 5 9 8 2 9 3 5 4 6 5 3 5 2 4 8 5 9 3输出 #2
3说明/提示
样例解释
样例 $1$ 解释:对于第一组测试用例,考虑序列 $[1,2]$,$a_1=4$、$b_1a_2 = 100$、$b_2=9$ 都是完全平方数,该序列长度为 $2$。$[4,5,3]$ 也是一个好序列,但是它的长度为 $3$,不是最优的。
数据范围
本题采用 Subtask 捆绑测试。
Subtask $\sum n \le$ 分数 $1$ $10$ $20$ $2$ $400$ $40$ $3$ $2000$ $40$ 对于所有数据,$1\le a_i, b_i \le 10^5$,$1 \le n, \sum n \le 2000$。
提示
$n$ 是完全平方数,当且仅当存在整数 $x$ 满足 $n=x^2$。
第 0 步:先看清要判什么
题目约束相邻两项:
$$b_{p_i}\cdot a_{p_{i+1}}\ \text{是完全平方数}\qquad(1\le i<m)$$
同时要求首项 a 是平方,末项 b 是平方。
由数对$(a_{p_i},b_{p_i})$很容易想到一条有向边$u_{a_i}$->$u_{b_i}$
所以将题目转化为"寻找一条最短路,要求入点、出点、边权均为平方数"
第 1 步:计算平方核 K(x)
直接计算边权的积非常难算,所以通过平方性质(即"平方由偶数个相同系数组成")引入平方核概念优化整张图:
定义:平方核$K(x)$表示只保留$K(x)$出现奇数次的质因子,每个取一次。
$$K(x)=\prod_{e_p(x)\ \text{为奇}} p$$
即:$K(x)=x \div (\text{能整除 } x \text{ 的最大平方数})$,"把所有能开方的部分开掉"。
| $x$ | 分解 | $K(x)$ | 说明 |
|---|---|---|---|
| 8 | $2^3$ | 2 | 指数 3 是奇,只取一个 2 |
| 12 | $2^2\cdot3$ | 3 | 4 被开掉 |
| 18 | $2\cdot3^2$ | 2 | 9 被开掉 |
| 4, 9, 16 | 平方数 | 1 | 全是偶数次 |
第 2 步:建图
根据两点的平方核乘积判断连通并建图:
- 点:每个输入位置(下标 $i$)(注意不是数值)。
- 边:$i\to j$ 当且仅当 $i\ne j$ 且 $K(b_i)=K(a_j)$(承接上一点)。
if (i != j && kb[i] == ka[j]) graph[i].push_back(j);
注:
- 整张图有向且为反向边:边表示"$j$ 接在 $i$ 后面",用的是 $i$ 的尾巴
b和 $j$ 的头a(写成ka[i]==kb[j])是反向边,原因:两二元组是否可以相连依据是$b_p·a_{p+1}$当且仅当剩余数(平方核)k[b[i]]==k[a[j]]才能相连,$a_p/a_{p+1}$与$b_p/b_{p+1}$本身就是连通的,不需要考虑建图连通性 - 必须 $i\ne j$:防止自环
第 3 步:多源 BFS
- 起点集合:$K(a_i)=1$ 的点(可以当序列第一个)。
- 终点集合:$K(b_i)=1$ 的点(可以当序列最后一个)。
- 所有起点一起入队,
dist初始化为 1,让距离直接等于序列长度。 - 出队时检查:
dist[u] > 1(防止自环)且 $K(b_u)=1$,第一个命中的就是最优解。
while (!q.empty()) {
int u = q.front();
q.pop();
if (dist[u] > 1 && kb[u] == 1) {
ans = dist[u];
break;
} // 命中终点
for (int v : graph[u])
if (dist[v] == -1) {
dist[v] = dist[u] + 1;
q.push(v);
}
}
复杂度
| 步骤 | 时间 | 空间 |
|---|---|---|
| 算核 | $O(n\sqrt{A})$ | $O(n)$ |
| 建图 | $O(n^2)$ | $O(n+E)$,$E\le n^2$ |
| BFS | $O(n+E)$ | $O(n)$ |
$n\le2000$,最坏约 $4\times10^6$ 条边,实测约 16 MB、几十毫秒,完全够用。
思维链:
相邻乘积是平方
↓ 只关心指数奇偶
平方核 K(x)
↓ $K(b_i) == K(a_j)$
有向边 i → j
↓ 多源 + 距离=点数
最短序列长度
源代码:
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
int lpf(int x){ //求平方核
int ans=1;
for(int i=2;i*i<=x;i++){
int m=0;
while(x%i==0){
x/=i;
m^=1;
}
if(m) ans*=i; //指数为奇,将该因子计入
}
if(x>1) ans*=x; //剩余质数一并乘入结果
return ans;
}
void solve(){
int n;
cin>>n;
int both=0;
vector<int> ks(n),ke(n);
for(int i=0;i<n;i++){
int st,ed;
cin>>st>>ed;
st=lpf(st);
ed=lpf(ed);
if(st==1&&ed==st){ //特殊形态:a,b均为平方数,只要有两个就可以直接返回2
both++;
}
ks[i]=st;
ke[i]=ed;
}
if(both>=2){
cout<<2<<'\n';
return;
}
vector<vector<int>> grid(n); //邻接表
vector<int> dist(n,-1); //记录最短距离/判断访问
queue<int> q;
for(int i=0;i<n;i++){
if(ks[i]==1){ //先判为平方数的a对应的点,直接入队
dist[i]=1;
q.push(i);
}
for(int j=0;j<n;j++){
if(i!=j&&ke[i]==ks[j]){ //非自环/首尾相乘为平方数->连通
grid[i].push_back(j);
}
}
}
int ans=-1;
while(!q.empty()){
int x=q.front();
q.pop();
if(ke[x]==1&&dist[x]>1){ //结尾点b为平方数,直接得答案
ans=dist[x];
break;
}
for(auto& nxt:grid[x]){
if(dist[nxt]==-1){ //继续遍历未访问点
dist[nxt]=dist[x]+1;
q.push(nxt);
}
}
}
cout<<ans<<'\n';
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t=1;
cin>>t;
while(t--){
solve();
}
return 0;
}
浙公网安备 33010602011771号