【题解】ABC 210
E.
题意:求最小生成树。
Solution:
数论+生成树。
我们将尝试快速模拟 Kruskal 算法的过程。
发现在第 i 个状态,假设操作前有 A 个联通块,操作后 B 个块,那么代价等于 (A-B) * c[i] 。
考虑 w 和 v 联通等价于:w=v+k1a1+k2a2+ ... +k_{n-1}a_{n-1},k_i \in Z 。这里根据裴蜀定理得到 w mod d = v mod d ,其中 d=gcd(a1,a2, ... an) 。
时间复杂度 O(nlogn) 。
#include<bits/stdc++.h>
#define mp make_pair
#define PII pair<ll,ll>
#define pb push_back
#define vec vector
#define All(x) x.begin(),x.end()
#define INS(x) inserter(x,x.begin())
#define INF 0x3f3f3f3f
#define ll long long
using namespace std;
//Task : Atcoder abc
//Author : cqbzly
ll n,m,cost,tot;
PII A[100005];
ll gcd(ll x,ll y) {
return (y==0)?x:gcd(y,x%y);
}
int main() {
cin>>n>>m;
for(int i=1;i<=m;i++) {
cin>>A[i].second>>A[i].first;
}
sort(A+1,A+1+m);
for(int i=1;i<=m;i++) {
ll cnt=gcd(A[i].second,n);
if(cnt==1) {
cost+=(n-1)*A[i].first;
cout<<cost;
return 0;
}
else {
cost+=(n-cnt)*A[i].first;
n=cnt;
}
}
cout<<"-1";
}
F.
这是一道 2-sat 的题目。
不妨用 vector<int> s[x] 表示有第 x 个质因子的位置 i 的集合。
然而暴力跑 2-sat 时间复杂度 O(n^2) 。(考虑每一个质因子 x ,将 s[x] 元素两两连边) 。
现在我们考虑减少语句数量。首先,我们加入 X_{p,1} , X_{p,2} , ... X_{p,pk} ,然后从 x 向 X_{p,i}=x 连边。这样便于在同一个质数集合内转移。
我们意识到没有必要两两连边,定义 Y_{p,1} 表示前面全为 0 ,同样与 Y_{p,i}=x 建立映射关系。这样的话变的总条数约为 7*\sum{siz_s[x]}。
如此时间复杂度 \sum{siz_s[x]}=O(nlogL) 。可以用 tarjan缩点 算法解决。
代码比较冗杂。题解运用了重新编号思想,值得学习。(本人写不出来。。。)
#include<bits/stdc++.h>
#include<atcoder/twosat>
#define ll long long
using namespace std;
//2-sat 算法:可以有向图 tarjan 缩点
//提供一个大常数建边思路。
int main() {
ios::sync_with_stdio(false);
int n;
cin>>n;
atcoder::two_sat ts(n+n*2*7);
vector<int> v((int)2e6,0);
int u=n;
for(int i=0;i<n;i++) {
int a[2];
cin>>a[0]>>a[1];
for(int j=0;j<2;j++) {
int t=a[j];
for(int l=2;l<=t;l++) {
//因为 t<=2e6, 所以不会暴 longlong
if(l*l>t) l=t;
if(t%l) continue;
while(t%l==0) t/=l;
if(v[l]>0) {
//若 x 为 0/1, 则 y 为 0/1
ts.add_clause(v[l],false,u,true);
ts.add_clause(i,j,v[l],false);
}
ts.add_clause(i,j,u,true);
v[l]=u++;
}
}
}
cout << (ts.satisfiable() ? "Yes" : "No") <<endl;
return 0;
}

浙公网安备 33010602011771号