每天遇到的奇妙方法集合

1. 摩尔投票法

洛谷 P2397

自从上次 redbag 用加法好好的***难过了 yyy 同学以后,yyy 十分愤怒。

他还击给了 redbag 一题,但是这题他惊讶的发现自己居然也不会,所以只好找你

一共有 nn 个正整数 aia_i,他让 redbag 找众数。他还特意表示,这个众数出现次数超过了一半。

摩尔投票法的基本思想很简单,在每一轮投票过程中,从数组中找出一对不同的元素,将其从数组中删除。这样不断的删除直到无法再进行投票,如果数组为空,则没有任何元素出现的次数超过该数组长度的一半。如果只存在一种元素,那么这个元素则可能为目标元素。

为了便于理解,我们想象一个情景,有一堆人在打架,我们假设每一个人的战斗力都是 1111。也就是 AA 打死 BBAA 也会死。

假设只有 22 波人(我们把不是众数的数字看作一个数)且各占二分之一,显然继续打下去两边人都会死光。

但是我们现在有 22 波人,其中一波人特别多(一半以上),显然这波人必然是赢家,虽然不知道打完还剩几个,但一定会有剩下的。因为一换一。若第二波人有x个,则第一波人有(nxn-x)个且 nx>xn-x>x。那么最后剩下的人就是 n2xn-2x 个。假设这 22 波人头上顶着 22 个不同的数字,这个剩下的人的数字就是众数。

以上用于帮助理解代码。

所以我们可以把摩尔投票法看作一种抵消的思想,当前数字进场,然后和上一个判断,如果是同一个数计数器++++,如果不是计数器--

当计数器等于 00 时,ansans 重新登记为新输入的数,进行抵消的操作,最后剩下的一定是众数(但众数必须满足大于总数的一半)。

#include <bits/stdc++.h>
using namespace std;
int n, x, ans, y = 0;
int main()
{
  scanf("%d", &n);
  for (int i = 1; i <= n; i++)
  {
    scanf("%d", &x);
    if (y == 0)
      ans = x;
    if (ans == x)
      y++;
    if (ans != x)
      y--;
  }
  printf("%d", ans);
  return 0;
}

2. 全源最短路

例题

Johnson 全源最短路

  • 建立一个虚点 00 号结点。

  • 00 号结点向 i(i[1,n])i(i\in[1,n]) 连接一条权值为 00 的有向边。

  • 使用 spfa 算法求出从 00 号结点到 i(i[1,n])i(i\in[1,n]) 的最短路 disidis_i

  • 由于 dijkstra 不能求负权最短路,所以将连接 u,vu,v 的边权 ww 更新为 w+disudisvw+dis_u-dis_v

  • 接下来对 i(i[1,n])i(i\in[1,n]) 号点进行 dijkstra,统计答案即可。

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef pair<int, int> pii;

#define int ll

const int N = 16666;
const int inf = 1e9;

struct Edge
{
    int nxt;
    int to;
    int w;
} e[N];

int head[N], edge_num = 0;

inline void add_edge(int x, int y, int z)
{
    e[++edge_num].to = y;
    e[edge_num].nxt = head[x];
    e[edge_num].w = z;
    head[x] = edge_num;
}

int n, m, indeg[N], inque[N];

int d[N];

inline bool SPFA(int s)
{
    for (int i = 1; i <= n; i++)
    {
        d[i] = inf;
    }
    memset(inque, false, sizeof(inque));

    queue<int> qwq;

    qwq.push(s);

    d[s] = 0, inque[s] = true;

    indeg[s]++;

    while (!qwq.empty())
    {
        int u = qwq.front();

        qwq.pop();

        inque[u] = false;

        for (int i = head[u]; i; i = e[i].nxt)
        {
            int v = e[i].to;

            if (d[v] > d[u] + e[i].w)
            {
                d[v] = d[u] + e[i].w;

                if (inque[v] == false)
                {
                    qwq.push(v);

                    inque[v] = true;

                    indeg[v]++;

                    if (indeg[v] >= n + 1)
                    {
                        return true;
                    }
                }
            }
        }
    }

    return false;
}

int dis[N];

bool vis[N];

inline void Dij(int s)
{
    for (int i = 1; i <= n; i++)
    {
        dis[i] = inf;
    }

    memset(vis, false, sizeof(vis));

    priority_queue<pii, vector<pii>, greater<pii>> q;

    dis[s] = 0;

    q.push(make_pair(0, s));

    while (!q.empty())
    {
        int u = q.top().second;

        q.pop();

        if (vis[u])
        {
            continue;
        }

        vis[u] = true;

        for (int i = head[u]; i; i = e[i].nxt)
        {
            int v = e[i].to;

            if (dis[v] > dis[u] + e[i].w)
            {
                dis[v] = dis[u] + e[i].w;

                if (vis[v] == false)
                {
                    q.push(make_pair(dis[v], v));
                }
            }
        }
    }
}

signed main()
{
    scanf("%lld%lld", &n, &m);
    for (int i = 1, x, y, z; i <= m; i++)
    {
        scanf("%lld%lld%lld", &x, &y, &z);

        add_edge(x, y, z);
    }

    for (int i = 1; i <= n; i++)
    {
        add_edge(0, i, 0);
    }

    if (SPFA(0) == true)
    {

        printf("-1");

        return 0;
    }

    for (int i = 1; i <= n; i++)
    {
        for (int j = head[i]; j; j = e[j].nxt)
        {
            int v = e[j].to;

            e[j].w += d[i] - d[v];
        }
    }

    for (int i = 1; i <= n; i++)
    {
        Dij(i);

        ll ans = 0;

        for (int j = 1; j <= n; j++)
        {
            if (dis[j] == inf)
            {
                ans += 1ll * inf * j;
            }

            else
            {
                ans += 1ll * j * (dis[j] - d[i] + d[j]);
            }
        }

        printf("%lld\n", ans);
    }

    return 0;
}

树的边权和等于第 ii 个叶子到第 i+1i+1 个叶子的距离(简单路径的长度)之和(特别的,第 n+1n+1 个叶子是第 11 个)除以 22

posted @ 2021-08-22 14:12  蒟蒻orz  阅读(1)  评论(0编辑  收藏  举报  来源