图论

图论

最小环

[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");
    }
}
posted @ 2020-03-28 00:11  瓜瓜站起来  阅读(176)  评论(0编辑  收藏  举报