zoukankan      html  css  js  c++  java
  • 同余最短路

    概念

    同余最短路是一种优化最短路建图的方法

    通常是解决给定 (m) 个整数,求这 (m) 个整数能拼凑出多少的其他整数(这 (m) 个整数可以重复取)或给定 (m) 个整数,求这 (m) 个整数不能拼凑出的最小(最大)的整数。

    栗题

    P2371 [国家集训队]墨墨的等式

    题目大意:

    对于 (sum_{i = 1}^na_ix_i = b) ,给定 (n)(a_{1…n})(l)(r) ,求有多少 (b in[l, r])​ 可以使得等式存在非负整数解

    (nleq 12,0leq a_ileq5*10^5,0leq lleq rleq 10^{12})

    solution

    把题目稍微变一下形

    求有多少组 (x_i) ,使得 (a_1*x_1 + a_2 * x_2…a_n*x_n = b) 成立

    发现这个题和货币系统那个题很像,考虑完全背包做法,但是这个题的 (l, r)​​ 很大,所以就有了同余最短路

    好像题解解释的都不是很清楚 ??这用分组的思想感觉解释好像能解释的更清楚点 = =

    对于这个题,我们可以先求出 ([1, l - 1])​​ 有多少 (b)​​ 满足条件,再求出 ([1, r])​​ 有多少 (b)​​​ 满足条件,作差就好了。

    转换一下,与其考虑 (b) 是否满足条件,不如考虑 (b)​ 是否能被凑出来。

    好,现在我们的目标就是统计 ([1,x])​​​​ 内有多少个数能被凑出来。

    考虑这么一件事:找出 (a_i)​​ 中最小的记为 (Min)​​ ,令 (p in[1, Min))

    我们所有的数按照对 (Min)​ 取模,按照余数分为 (Min)​​ 类。

    如果 (p + k*Min)​​​​ 能够被凑出来,那么 (p + (k + N^*) * Min)​​​​ 也一定能被凑出来,也就是从这之后,这一类都能被凑出来了,令 (tmp = p + k_{min} * Min)​​ ,也就是这一类最早能被凑出来的数。如果 (tmp leq x)​​​,那么这个区间关于这一类能凑出来的数的个数就是 (frac{x - tmp}{Min} + 1)​​ ,求所有类就是对每一类数做相同操作加和就好了。

    现在就只剩怎么求每一类被第一次凑出来的数是多少。

    把它放在图里,让 (p)​​​ 向 ((p +a_i) \% Min)​​​ 连一条权值为 (a_i)​​​ 有向边,也就是说凑出 (p + a_i)​​​ 的代价为 (a_i)​​​​ ,从 (0)​ 点到 ([0, Min))​ 的最短路长度就是每一类第一次被凑出来的数是多少。

    然后就完成啦。

    code

    /*
    work by:Ariel_
    */
    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <queue>
    #include <algorithm>
    #define int long long
    #define rg register
    using namespace std;
    const int N = 5e5 + 5;
    const int M = 6e6 + 5;
    const int INF = 0x3f3f3f3f3f3f;
    int read(){
        int x = 0,f = 1; char c = getchar();
        while(c < '0'||c > '9') {if(c == '-') f = -1; c = getchar();}
        while(c >= '0' && c <= '9') {x = x*10 + c - '0'; c = getchar();}
        return x*f;
    }
    int n, l, r, a[N], m, Min = INF, dis[N], vis[N];
    struct edge {int v, nxt, w;}e[M]; 
    int head[N], E;
    void add_edge(int u, int v, int w) {
       e[++E] = (edge) {v, head[u], w};
       head[u] = E;
    }
    queue<int> q;
    void SPFA(int s) {
       memset(dis, INF, sizeof dis); 
       dis[s] = 0, q.push(s);
       while(!q.empty()) {
       	  int u = q.front(); q.pop(); vis[u] = 0;
       	      for (rg int i = head[u]; i; i = e[i].nxt){
       	        int v = e[i].v, w = e[i].w;
       	  	    if (dis[v] > dis[u] + e[i].w){	
    			   dis[v] = dis[u] + e[i].w;
    			   if (!vis[v]) q.push(v), vis[v] = 1;
    		    }
    	    }
       }
    }
    int query(int x) {
        int res = 0;
        for (rg int i = 0; i < Min; i++) 
           if (dis[i] <= x)
           	  res += (x - dis[i]) / Min + 1;
    	return res;
    }
    signed main(){
       n = read(), l = read(), r = read();
       for (rg int i = 1, x; i <= n; i++) {
       	   x = read();
           if (x) a[++m] = x, Min = min(x, Min);//0 边没有贡献 
       }
       n = m;
       for (rg int i = 0; i < Min; i++) 
       	  for (rg int j = 1; j <= n; j++)
       	      if (a[j] != Min) add_edge(i, (i + a[j]) % Min, a[j]);
       SPFA(0);
       printf("%lld", query(r) - query(l - 1));
       puts("");
       return 0;
    }
    

    这道题的弱化版 P3403 跳楼机

    注意这道题是从一楼开始,想想与上面板子比较哪里变了 = =

    (finish)

  • 相关阅读:
    决策树分类
    集群服务器 获取COOKIE错误
    React 自写Loading
    HTB-靶机-Unattended
    HTB-靶机-LaCasaDePapel
    HTB-靶机-FriendZone
    HTB-靶机-CTF
    HTB-靶机-FluJab
    HTB-靶机-Help
    HTB-靶机-Chaos
  • 原文地址:https://www.cnblogs.com/Arielzz/p/15049788.html
Copyright © 2011-2022 走看看