图论
图论
最小环
[CF1325E]数论+最小环
一个数最多只有7个因子,意味着它是\(a*a*b\)或者\(a*b\)
给定一个序列,每个数都最多只有7个因子,需要找到最短序列的长度,使得序列元素的乘积是完全平方数,且\(a_i \leq 10^6\)
思路:如果是\(a*a\),则答案为1
如果是\(a*a*b\),把它变成\(b*1\),建一条\(b\)与\(1\)的连边
如果是\(a*b\),建一条\(b\)与\(a\)的连边
(去重)
找到最小环
在考虑直接用最小环会超时
发现如果有一个这样的序列,那么一定有一个点,小于\(\sqrt{10^6}\)
那么只要枚举这1000个点即可(其实是小于1000的质数)
注意:这题保证了至多有两个质因子
(From God Yan)
注意这个数字分解的方法和最小环的求解
第一份代码:大概在1500ms左右
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 100;
const int inf = 0x3f3f3f3f;
int a[maxn];
int p[maxn];
long long dis[maxn];
vector<int> v, e[maxn];
void init() {
for (int i = maxn - 1; i >= 2;i--) {
for (int j = i; j < maxn;j+=i) {
p[j] = i;
}
}
}
int main() {
int n;
scanf("%d", &n);
init();
for(int i=1;i<=n;i++) {
int x;scanf("%d",&x);
int a = 1, b = 1;
while(x!=1) {
int t = p[x];
if(x%(t*t)==0) {
x /= (t * t);
}
else {
x /= t;
if(a==1)
a = t;
else
b = t;
}
}
e[a].push_back(b);
e[b].push_back(a);
v.push_back(a);
v.push_back(b);
}
sort(v.begin(), v.end());
int sz = unique(v.begin(), v.end())-v.begin();
long long ans = inf;
for(int x:v) {
if(x>1000)
break;
queue<int> q;
for (int i = 0; i < sz;i++) {
dis[v[i]] = inf;
}
dis[x] = 0;
q.push(x);
while(!q.empty()) {
int t = q.front();
q.pop();
for(int to:e[t]) {
if(dis[to]>dis[t]+1) {
dis[to] = dis[t] + 1;
q.push(to);
}
else if(dis[to]>=dis[t]) {
ans = min(ans, dis[to] + dis[t] + 1);
}
}
}
}
if(ans>=inf) {
cout << -1 << endl;
}
else {
cout << ans << endl;
}
}
第二份代码:187ms
1.首先
if(dis[to]+dis[t]>=ans) continue;
这一行剪枝可以优化一倍左右
2.所有节点都是质因子且没有重边
但是要注意两个特判
3.也可以把去重的那一部分删掉,时间大概是320ms
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 100;
const int inf = 0x3f3f3f3f;
int a[maxn];
int p[maxn];
long long dis[maxn];
vector<int> v, e[maxn];
int pm[maxn],pid[maxn];
int ptop;
void init() {
int n = maxn - 1;
p[0] = 1;
pm[1] = 1;
pid[1] = 0;
for(int i = 2; i <= n; ++i) {
if(!pm[i]) {
p[++ptop] = i;//新增一个质数
pm[i] = i;//质数的因子是自己
pid[i] = ptop;//这个质数的序号
}
for(int j = 1; j <= ptop; ++j) {
int t = i * p[j];
if(t > n)
break;
pm[t] = p[j];
if(i % p[j] == 0)
break;
}
}
}
int fj(int x) {
int d = 1;
while(x > 1) {
int p = pm[x];
x /= p;
if(d % p == 0) {
d /= p;
continue;
}
d *= p;
}
return d;
}
int main() {
int n;
scanf("%d", &n);
init();
for(int i=1;i<=n;i++) {
scanf("%d", &a[i]);
a[i] = fj(a[i]);
}
sort(a + 1, a + 1 + n);
int m = unique(a + 1, a + 1 + n) - (a + 1);
if(a[1]==1) {
cout << 1 << endl;
return 0;
}
if(m<n){
cout << 2 << endl;
return 0;
}
for (int i = 1;i<=m;i++) {
int x = pid[pm[a[i]]], y = pid[pm[a[i] / pm[a[i]]]];
e[x].push_back(y);
e[y].push_back(x);
}
long long ans = inf;
for (int i = 0; i < ptop, p[i] <= 1000;i++) {
int x = pid[p[i]];
queue<int> q;
for (int i = 0; i < ptop;i++) {
dis[i] = inf;
}
dis[x] = 0;
q.push(x);
while(!q.empty()) {
int t = q.front();
q.pop();
for(int to:e[t]) {
if(dis[to]>dis[t]+1) {
dis[to] = dis[t] + 1;
if(dis[to]+dis[t]>=ans)
continue;
q.push(to);
}
else if(dis[to]>=dis[t]) {
ans = min(ans, dis[to] + dis[t] + 1);
}
}
}
}
if(ans>=inf) {
cout << -1 << endl;
}
else {
cout << ans << endl;
}
}
[CF1190E] Matching vs Independent Set 图匹配
https://codeforces.com/contest/1199/problem/E
给定一个3*n个点m条边的简单无向图,要求在这个图里找出一个有n条边的独立边集
或者找出一个有n个点的独立点集,并且输出答案
#include <bits/stdc++.h>
#include<stdint.h>
#define int long long
#define scan(n) scanf("%lld", &(n))
#define scann(n, m) scanf("%lld%lld", &(n), &(m))
#define scannn(a, b, c) scanf("%lld%lld%lld", &(a), &(b), &(c))
#define prin(n) printf("%lld", (n))
#define pb push_back
#define mp make_pair
#define ms(a) memset(a, 0, sizeof(a))
#define fo(i, a, b) for (int i = (a); i <= (b); i++)
#define ro(i, a, b) for (int i = (a); i >= (b); i--)
const int inf = 0x3f3f3f3f;
using namespace std;
const int maxn = 3e5+100;
int n,m;
int u[maxn],v[maxn];
int vis[maxn];
int32_t main() {
int T;scan(T);
while(T--){
scann(n,m);
fo(i,1,3*n)vis[i]=0;
vector<int>edge_ans;
fo(i,1,m){
int u,v;scann(u,v);
if(vis[u]||vis[v])continue;
vis[u]=1,vis[v]=1;
edge_ans.pb(i);
}
if(edge_ans.size()>=n){
printf("Matching\n");
fo(i,0,n-1){
printf("%lld ",edge_ans[i]);
}
}
else{
printf("IndSet\n");
int cnt=0;
fo(i,1,3*n){
if(cnt>=n)break;
if(!vis[i])printf("%lld ",i),cnt++;
}
}
printf("\n");
}
return 0;
}
Distance in Tree
DFS
[CF977E] dfs判环
187ms 用dfs找连通块,并且需要环上的每个点的度为2
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 5;
int vis[maxn];
int ans, f;
vector<int> e[maxn];
void dfs(int u) { //遍历每个联通块
vis[u] = 1;
if(e[u].size()!=2)
f = 1;
for(int v : e[u]) {
if(!vis[v])
dfs(v);
}
}
int main() {
int n, m;
scanf("%d%d",&n,&m);
for (int i = 1; i <= m;i++) {
int u,v;
scanf("%d%d",&u,&v);
e[v].push_back(u);
e[u].push_back(v);
}
for(int i=1;i<=n;i++) {
f = 0;
if(!vis[i]) {
dfs(i);
if(f==0)ans++;
}
}
printf("%d\n", ans);
}
93ms 并查集
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 5;
int f[maxn];
int d[maxn];
struct edge{
int u, v;
}e[maxn];
int find(int x) {
return f[x] == x ? x : f[x] = find(f[x]);
}
int main() {
int n,m;scanf("%d%d",&n,&m);
for(int i=0;i<=n;i++)
f[i] = i;
for(int i=1;i<=m;i++) {
scanf("%d%d", &e[i].u, &e[i].v);
d[e[i].u]++;
d[e[i].v]++;
}
int ans = 0;
for(int i=1;i<=m;i++) {
if(d[e[i].u]==2&&d[e[i].v]==2) {
int a = find(e[i].u);
int b = find(e[i].v);
if(a==b)
ans++;
else if(a<b)
f[b] = a;
else
f[a] = b;
}
}
cout << ans << endl;
}
Counting cliques DFS
include <bits/stdc++.h>
include<stdint.h>
using namespace std;
define int long long
define scan(n) scanf("%lld", &(n))
define scann(n, m) scanf("%lld%lld", &(n), &(m))
define scannn(a, b, c) scanf("%lld%lld%lld", &(a), &(b), &(c))
define pb push_back
define fo(i, a, b) for (int i = (a); i <= (b); i++)
const int inf = 0x3f3f3f3f;
const int maxn = 2e5+100;
int n,m,s,x,y,pre[20],e120,ans,tot,head[120];
struct node{
int v,nxt;
}a[1024];
void add(int u,int v){
tot++;
a[tot].v=v;
a[tot].nxt=head[u];
head[u]=tot;
}
void dfs(int u,int step){
pre[step]=u;
if(step==s){ans++;return;}
for(int j=head[u];~j;j=a[j].nxt){
int x=a[j].v;
int f=1;
fo(i,1,step){
if(epre[i]==0){f=0;break;}
}
if(f){
step++;
dfs(x,step);
step--;
}
}
return;
}
int32_t main() {
int T;scan(T);
while(T--){
scannn(n,m,s);
fo(i,0,15)pre[i]=0;
fo(i,0,105)fo(j,0,105)ei=0;
fo(i,0,105)head[i]=-1;
ans=0,tot=0;
fo(i,1,m){
scann(x,y);
if(x>y)swap(x,y);
ex=1;add(x,y);
}
fo(i,1,n)dfs(i,1);
printf("%lld\n",ans);
}
return 0;
}
BFS
矩阵距离
https://ac.nowcoder.com/acm/contest/1017/B
01矩阵,求每个0最近的1的曼哈顿距离
直接将所有1点加入队列即可
这样每次都会更新同一深度的所有0
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1000+5;
int dx[4] = {0, 0, 1, -1}, dy[4] = {1, -1, 0, 0};
char s[maxn][maxn];
int d[maxn][maxn];
int n, m;
struct node{
int x, y;
};
queue<node> q;
bool ok(int x,int y) {
if(x>n||y>m||x<1||y<1)
return false;
return true;
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n;i++) {
scanf("%s", s[i] + 1);
}
for (int i = 1; i <= n;i++) {
for (int j = 1;j<=m;j++) {
if(s[i][j]=='0')
d[i][j] = -1;
else
q.push((node){i, j});
}
}
while(!q.empty()) {
node t = q.front();
q.pop();
int x = t.x;
int y = t.y;
for (int i = 0; i < 4;i++) {
int xx = x + dx[i];
int yy = y + dy[i];
if(!ok(xx,yy))
continue;
if(d[xx][yy]==-1) {
d[xx][yy] = d[x][y] + 1;
q.push((node){xx, yy});
}
}
}
for (int i = 1; i <= n;i++) {
for (int j = 1; j <= m;j++) {
printf("%d ", d[i][j]);
}
printf("\n");
}
}