2024/1/14 算法笔记

1. 图论的反向建边

一般问题:有向图的多个起点到一个终点的最短距离 是最短路的变式。

我们只需要把图的箭头反向(正向变逆向,逆向变正向)

矩阵: mp[u,v] = cost ---->mp[v,u] = cost
邻接表也是类似的方法

[P2853 USACO06DEC] Cow Picnic S - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

这里为什么要反向建边,因为我们要牧场去找牛,相反的,我们可以让含有牛的牧场去找牧场。

2. DAG图的求点1到点n两点之间的最长路

关键问题是可能不止有点1是入度为0的点。如果不删除这些点,我们无法做到通过删点边的方式来求到1到n的路径。所以应该先遍历一次从点2到点n的入度为0的点找到那些点,再把延伸出来的点的入度 −1,如果这些点入读 −1后又变成了入度为 0 的点,那么再做同样的处理。至于一个点的最长路的转移方程就是:
max{入度1+相应的边,入度2+相应的边……入度n加相应的边} 类似于dijkstra的更新距离操作。

vector<int>g[1505];
vector<int>d[1505];
queue<int>q;
int v[1505];//存最长路
int n,m;
int ru[1505];

void solve(){
    cin>>n>>m;
    for(int i=1;i<=m;i++){
        int u,v,val;
        cin>>u>>v>>val;
        g[u].eb(v);
        d[u].eb(val);
        ru[v]++;
    }

    for(int i=2;i<=n;i++){
        v[i] = -inf;
        if(!ru[i]) q.push(i);
    }

    while(!q.empty()){
        int x = q.front();
        q.pop();
        for(int i=0;i<g[x].size();i++){
            if(!(--ru[g[x][i]])) q.push(g[x][i]);
        }
    }

    q.push(1);
    while(!q.empty()){
        int x = q.front();
        q.pop();
        for(int i=0;i<g[x].size();i++){
            if(v[g[x][i]]<v[x]+d[x][i]) v[g[x][i]] = v[x]+d[x][i];
            if(!(--ru[g[x][i]])) q.push(g[x][i]);
        }
    }

    if(v[n] == -inf) cout<<"-1"<<endl;
    else cout<<v[n]<<endl;
}

3. m条直线交点(三点不重合)的情况数计算

n=4 的情况:

1)4 条直线全部平行,则 0 交点 { =4*(4-4)}。

2)其中 3 条直线平行,则 3 交点 { =3*(4-3) }。

3)其中 2 条直线平行,则这2条直线与另2条直线的交点数为4,而另2条直线之间可能有0个或1个交点(见 n=2 的情况,共 4 个交点或 5 个交点。{=2*(4-2)+0 或 1 }

4)4 条直线均不平行(可看成 1 条直线平行),这 1 条直线与其它 3 条直线的交点数为 3,而其 它 3 条直线之间的交点数为 3,共 6 个交点。{ =1*(4-1)+3 }

结论:m 条直线的交点方案=r 条平行线与(m-r)条直线交叉的交点数+(m-r)条直线本身的交点方案

=r*(m-r)+(m-r)条直线本身的交点方案 (1<=r<=m)

  • 写成递归程序
void g(int n,int k){  //交点数k   n是平行线数量
    if(n==0) {f[k]=1;maxn = max(maxn,k);}
    else {
        for(int r=n;r>=1;r--){
            g(n-r,(n-r)*r+k);
        }
    }
}

4. 最大公约数,最小公倍数的一些结论

  1. 求gcd() , 方法一是直接调用c++的库中的gcd函数。方法二:
ull gcd(ull a, ull b)
{
    return b == 0 ? a : gcd(b, a % b);
}
  1. 求lcm(),方法是a*b/gcd(a,b)
ull lcm(ull a, ull b)
{
    return a / gcd(a, b) * b;  //这样写是防止a*b溢出。
}
  1. 一些固有知识:gcd(x,a0) = a1 ---> gcd(x/a1 , a0/a1) = 1 暂且叫它为结论p

    lcm(x,b0) = b1 = x*b0/gcd(x,b0)

    使用结论p我们可以得到:gcd(x , b0) = x*b0/b1 ----> gcd(b1/b0 , b1/x) = 1 结论q

    用心体会这两个式子,你会发现x是a1的整数倍而且是b1的因子

    如果我们知道a0,a1,b0,b1想要求x的个数,我们可以遍历1~sqrt(b1) 找到能整除的数,且满足结论p和结论q,就能找到x的情况了。

    int gcd(int a,int b){
        return b==0?a:gcd(b,a%b);
    }
    
    int a0,a1,b0,b1;
    void solve(){
        int t;
        cin>>t;
        while(t--){
            cin>>a0>>a1>>b0>>b1;
            int p = a0/a1;
            int q = b1/b0;
            int cnt = 0;
            for(int i=1;i*i<=b1;i++){
                if(b1%i==0){
                    if(i%a1==0&&gcd(i/a1,p)==1 && gcd(q,b1/i)==1) cnt++;
                    int j = b1/i;
                    if(i==j) continue;
                    if(j%a1==0&&gcd(j/a1,p)==1&& gcd(q,b1/j)==1) cnt++;
                }
            }
            cout<<cnt<<endl;
        }
    }
    
posted @ 2024-01-14 22:49  Akimizuss101  阅读(22)  评论(0编辑  收藏  举报