标签: 最短路

  • CFGYM102394A CCPC2019哈尔滨A Artful Paintings

    一眼看过去是差分约束板子题,实际上也就是差分约束板子题。

    首先这个题可以对答案进行二分。如果铺x个可行,那么x+1个显然可行,所以我们先二分确定一个x。

    然后我们的题目转变成了,能不能在恰好铺x个的情况下构造一组合法方案,根据最短路的松弛条件可知,不等式$x_a+v\leq x_b$等价于从a到b,边权为$-v$的边(最短路存在时,令$x_a$表示源点到a的距离,$x_b$同理,那么$x_a$和$x_b$在作为未知变量的时候一定是满足上述不等式的。)

    所以我们可以得到,需要建立以下六种不等式:

    1. $x_{n-1}+0\leq x_n$(前缀和不减)
    2. $x_n+(-1)\leq x_{n-1}$前缀和要么加1要么不变
    3. $x_{l-1}+x\leq x_r$ 区间染色数下限
    4. $x_r +(x-M) \leq  x_{l-1}$ 区间外染色数下限
    5. $x_0+M\leq x_n$ 一共至少染M个
    6. $x_n+(-M)\leq x_0$ 一共至多染M个(与5一起保证恰好染M个)

    然后据此建边,其中$M$是我们二分的铺的个数。建出来的图跑一个从n到0的最短路,如果最短路存在(即没有负环),那说明M可行,答案可以考虑进一步缩小。如果存在负环,则说明M不可行。

    但是这里的SPFA要注意优化,大概需要一个SLF(Small Label First),即如果待入队节点的已知最短距离小于队列头部节点,则把他扔到队列头部。
    然后别用long long,跑的有点慢。
    #include <algorithm>
    #include <deque>
    #include <iostream>
    #include <vector>
    using std::cin;
    using std::cout;
    using std::endl;
    
    using int_t = int;
    
    /*
    1. x_{n-1}+0<=x_n(前缀和不减)
    2. x_n+(-1)<=x_{n-1}前缀和要么加1要么不变
    3. x_{l-1}+x<=x_r 区间染色数下限
    4. x_r +(x-M) <= x_{l-1} 区间外染色数下限
    5. x_0+M<=x_n 一共至少染M个
    6. x_n+(-M)<=x_0 一共至多染M个(与5一起保证恰好染M个)
    
    */
    
    const int_t LARGE = 3010;
    const int_t INF = 0x3fffffff;
    int_t dis[LARGE + 1];
    int_t inqN[LARGE];
    bool inqueue[LARGE + 1];
    struct Edge {
        int_t to, weight;
        Edge(int_t v, int_t w) : to(v), weight(w) {}
    };
    struct Limitation {
        int_t left, right, val;
    } cond1[LARGE + 1], cond2[LARGE + 1];
    
    int_t n, m1, m2;
    //不等式A+X<=B变为边(A,B),长度-X
    std::vector<Edge> graph[LARGE + 1];
    
    void clear() {
        std::fill(dis, dis + 1 + n, INF);
        std::fill(inqueue, inqueue + 1 + n, false);
        std::fill(inqN, inqN + 1 + n, 0);
        for (int_t i = 0; i <= n; i++)
            graph[i].clear();
    }
    
    void pushEdge(int_t u, int_t v, int_t w) {
    #ifdef DEBUG
        cout << "edge " << u << " to " << v << " val " << w << endl;
    #endif
        graph[u].push_back(Edge(v, w));
    }
    //添加不等式x_a+v<=x_b
    void pushLEQ(int_t a, int_t b, int_t v) {
    #ifdef DEBUG
        cout << "cond x_" << a << " + " << v << " <= x_" << b << endl;
    #endif
        pushEdge(b, a, -v);
    }
    
    bool SPFA(int_t src, int_t target) {
        std::deque<int_t> queue;
        dis[src] = 0;
        inqueue[src] = true;
        queue.push_back(src);
        while (!queue.empty()) {
            int_t front = queue.front();
            queue.pop_front();
            inqueue[front] = false;
            inqN[front]++;
            if (inqN[front] > n)
                return false;
            for (const auto& edge : graph[front]) {
                if (dis[edge.to] > dis[front] + edge.weight) {
                    dis[edge.to] = dis[front] + edge.weight;
    #ifdef DEBUG
                    cout << "dis " << edge.to << " to " << dis[edge.to] << endl;
    #endif
                    if (!inqueue[edge.to]) {
                        if (!queue.empty() && dis[edge.to] < dis[queue.front()])
                            queue.push_front(edge.to);
                        else
                            queue.push_back(edge.to);
                        inqueue[edge.to] = true;
                    }
                }
            }
        }
        return true;
    }
    
    bool check(int_t x) {
    #ifdef DEBUG
        cout << "checking " << x << endl;
    #endif
        clear();
        for (int_t i = 1; i <= n; i++) {
            pushLEQ(i - 1, i, 0);
        }
        for (int_t i = 1; i <= n; i++) {
            pushLEQ(i, i - 1, -1);
        }
        for (int_t i = 1; i <= m1; i++) {
            const auto& ref = cond1[i];
            pushLEQ(ref.left - 1, ref.right, ref.val);
        }
        for (int_t i = 1; i <= m2; i++) {
            const auto& ref = cond2[i];
            pushLEQ(ref.right, ref.left - 1, ref.val - x);
        }
        pushLEQ(0, n, x);
        pushLEQ(n, 0, -x);
        auto ret = SPFA(n, 0);
    #ifdef DEBUG
        cout << "check " << x << " = " << ret << endl;
    #endif
        return ret;
    }
    
    int main() {
        std::ios::sync_with_stdio(false);
        int_t T;
        cin >> T;
        while (T--) {
            cin >> n >> m1 >> m2;
            for (int_t i = 1; i <= m1; i++) {
                auto& ref = cond1[i];
                cin >> ref.left >> ref.right >> ref.val;
            }
            for (int_t i = 1; i <= m2; i++) {
                auto& ref = cond2[i];
                cin >> ref.left >> ref.right >> ref.val;
            }
            int_t left = 0, right = n;
            int_t result = INF;
            while (left + 1 < right) {
                int_t mid = (left + right) / 2;
                if (check(mid)) {
                    result = std::min(result, mid);
                    right = mid - 1;
                } else
                    left = mid + 1;
            }
            for (int_t i = left; i <= right; i++)
                if (check(i))
                    result = std::min(result, i);
            cout << result << endl;
        }
    
        return 0;
    }

     

  • 洛谷4001 狼抓兔子

    平面图最小割转对偶图最短路。

    #include <algorithm>
    #include <iostream>
    #include <queue>
    #include <vector>
    
    using int_t = long long int;
    using std::cin;
    using std::cout;
    using std::endl;
    
    const int_t LARGE = 2e6 + 10;
    const int_t SOURCE = 2e6 + 1;
    const int_t SINK = 2e6 + 2;
    const int_t INF = 0x3fffffffffffffff;
    int n, m;
    struct Edge {
        int to, weight;
    };
    
    std::vector<Edge> graph[LARGE + 1];
    
    int_t dis[LARGE + 1];
    bool visited[LARGE + 1];
    
    void pushEdge(int from, int to, int weight) {
        graph[from].push_back(Edge{to, weight});
        graph[to].push_back(Edge{from, weight});
    #ifdef DEBUG
        cout << "edge " << from << "," << to << "," << weight << endl;
    #endif
    }
    int_t encode(int_t r, int_t c) { return (r - 1) * m + c; }
    int main() {
        std::ios::sync_with_stdio(false);
        cin >> n >> m;
        if (m == 1) {
            int_t val = INF;
            for (int_t i = 1; i <= n; i++) {
                int_t x;
                cin >> x;
                val = std::min(val, x);
            }
            cout << val << endl;
            return 0;
        }
        for (int_t i = 1; i <= n; i++) {
            for (int_t j = 1; j < m; j++) {
    #ifdef DEBUG
                cout << i << "," << j << endl;
    #endif
                int_t val;
                cin >> val;
                if (i == 1) {
                    pushEdge(2 * encode(i, j) + 1, SINK, val);
                }
                if (i == n) {
                    pushEdge(SOURCE, 2 * encode(i - 1, j), val);
                }
    
                if (i != 1 && i != n) {
                    pushEdge(encode(i, j) * 2 + 1, encode(i - 1, j) * 2, val);
                }
            }
        }
        for (int_t i = 1; i < n; i++) {
            for (int_t j = 1; j <= m; j++) {
                int_t val;
                cin >> val;
                if (j == 1) {
                    pushEdge(SOURCE, 2 * encode(i, j), val);
                }
                if (j == m) {
                    pushEdge(encode(i, j - 1) * 2 + 1, SINK, val);
                }
                if (j != 1 && j != m) {
                    pushEdge(encode(i, j - 1) * 2 + 1, encode(i, j) * 2, val);
                }
            }
        }
        for (int_t i = 1; i < n; i++) {
            for (int_t j = 1; j < m; j++) {
                int_t val;
                cin >> val;
                pushEdge(encode(i, j) * 2, encode(i, j) * 2 + 1, val);
            }
        }
        std::fill(dis, dis + 1 + LARGE, INF);
        struct Pair {
            int_t vtx, dis;
            bool operator>(const Pair& p) const { return dis > p.dis; }
        };
        std::priority_queue<Pair, std::vector<Pair>, std::greater<Pair> > heap;
        heap.push(Pair{SOURCE, 0});
        dis[SOURCE] = 0;
        while (heap.empty() == false) {
            int front = heap.top().vtx;
            heap.pop();
            if (visited[front]) continue;
            visited[front] = true;
    #ifdef DEBUG
            cout << "poping " << front << endl;
    #endif
            for (const Edge& edge : graph[front]) {
                dis[edge.to] = std::min(dis[edge.to], dis[front] + edge.weight);
                heap.push(Pair{edge.to, dis[edge.to]});
            }
        }
        cout << dis[SINK] << endl;
        return 0;
    }

     

  • 洛谷3953 NOIP2017 逛公园

    $$ \text{考虑记忆化搜索} \\ \text{设}dis_u\text{表示从}u\text{到终点的最短距离} \\ \text{记录状态为}f\left( u,k \right) \text{表示从点}u\text{出发,到终点距离不超过}dis_u+k\text{的方案数} \\ \text{边界条件:} \\ \text{走到终点时,不管}k\text{是多少,方案数都会}+\text{1,同时继续向下搜索} \\ \text{要试图遍历一个已经在栈中的状态的时候说明走到了零环(零环上最短路不会变化)} \\ \text{转移的时候,对于一条边}\left( u,v,w \right) \ \text{考虑如果走这条边,我们比最短路又远了多少(肯定不会比最短路近)} \\ \text{首先如果}\left( u,v,w \right) \text{这条边在一条最短路上,那么显然满足}dis_v+w=dis_u\text{即}dis_v+w-dis_u=0 \\ \text{如果这条边不在最短路上,就说明走了这条边,到终点一定会花费比最短路更长的距离} \\ \text{即满足}x=dis_v+w-dis_u>0 \\ \text{这个时候如果走了这条边,那么我们就离最短路又远了}x\text{,所以,}f\left( u,k \right) \text{中可以直接} \\ \text{统计上}f\left( v,k-x \right) \\ \text{最后的答案为}f\left( \text{1,}k \right) $$

    #include <iostream>
    #include <algorithm>
    #include <cstdio>
    #include <vector>
    #include <inttypes.h>
    #include <queue>
    #include <functional>
    #include <cstring>
    #define debug(x) std::cout << #x << " = " << x << std::endl;
    
    typedef long long int int_t;
    
    using std::cin;
    using std::endl;
    using std::cout;
    
    const int_t LARGE = 100000;
    const int_t INF = 0x7fffffff;
    struct Processor{
    	struct Edge{
    		int_t to;
    		int_t weight;
    		Edge(int_t to,int_t weight){
    			this -> to = to;
    			this -> weight = weight;
    		}
    	};
    	struct Pair{
    		int_t dis;
    		int_t vertex;
    		Pair(int_t dis,int_t vtx){
    			this -> dis = dis;
    			this -> vertex  = vtx;
    		}
    		bool operator <(const Pair& x) const{
    			return dis < x.dis; 
    		}
    		bool operator > (const Pair& x) const{
    			return dis > x.dis;
    		}
    	};
    	std::vector<Edge> graph[LARGE + 1];
    	std::vector<Edge> rgraph[LARGE + 1];
    	bool visited[LARGE + 1][51];
    	bool visiting[LARGE + 1][51];
    	int_t memory[LARGE + 1][51];
    	int_t dis[LARGE + 1];
    	int n,m,k,p;
    	void pushEdge(int_t from,int_t to,int_t weight){
    		graph[from].push_back(Edge(to,weight));
    		rgraph[to].push_back(Edge(from,weight));
    	}
    	int_t search(int_t vtx,int_t k){
    		if(k < 0) return 0;
    //		cout<<"searching "<<vtx<<" "<<k<<" "<<endl;
    		if(visited[vtx][k]) return memory[vtx][k];
    		if(visiting[vtx][k]) return -1;
    		visiting[vtx][k] = true;
    		int_t result = 0;
    		if(vtx == n) result = 1;
    		for(int_t i = 0 ;i < graph[vtx].size(); i ++){
    			Edge& curr = graph[vtx][i];
    			int_t x = search(curr.to,k - (dis[curr.to] + curr.weight - dis[vtx]));
    			if(x == -1){
    				result = -1;
    				break;
    			}
    			result = result + x;
    			result %= p;
    		}
    		visited[vtx][k] = true;
    		visiting[vtx][k] = false;
    		return memory[vtx][k] = result;
    	}
    	int_t process(){
    		static bool visited[LARGE + 1];
    		memset(visited,0,sizeof(visited));
    		memset(visiting,0,sizeof(visiting));
    		scanf("%d%d%d%d",&n,&m,&k,&p);
    		std::fill(dis + 1 ,dis + 1 + n,INF);
    		std::fill(visited + 1 ,visited + 1 + n,false);
    		
    		for(int_t i = 1; i <= m;i++){
    			int from,to,weight;
    			scanf("%d%d%d",&from,&to,&weight);
    			pushEdge(from,to,weight);
    		}	
    		dis[n]  = 0;
    		std::priority_queue<Pair,std::vector<Pair>,std::greater<Pair> > heap;
    		heap.push(Pair(0,n));
    		while(heap.empty() == false){
    			Pair top =  heap.top();
    			heap.pop();
    			if(visited[top.vertex]) continue;
    			visited[top.vertex] = true;
    			for(int_t i = 0;i< rgraph[top.vertex].size();i ++){
    				Edge& curr = rgraph[top.vertex][i];
    				if(dis[curr.to] > dis[top.vertex] + curr.weight){
    					dis[curr.to] = dis[top.vertex] + curr.weight;
    					heap.push(Pair(dis[curr.to],curr.to));
    				}
    			}
    		}
    		return search(1,k);
    //		for(int_t i = 1;i <= n;i++){
    //			cout<<"dis "<<i<<" = "<<dis[i] << endl;
    //		}
    	}
    };
    
    int main() {
    	int T;
    	scanf("%d",&T);
    	for(int_t i = 1;i <= T;i++){
    		Processor * p =new Processor;
    		cout << p->process() << endl;
    		delete p;
    	}
        return 0;
    }

     

  • 洛谷1608 路径统计

    最短路计数模板。

    注意这道题要去掉重边造成的影响。

    对于每个点,记录一下从起点出发,到达这个点的最短路条数。

    转移的时候,如果访问到了一个未访问的点,或者松弛操作成功,那就直接转移过去。

    否则,如果遇到了一个访问过的点,那就看一下到这个点的距离是否等于当前点的距离+边权,是的话就说明从当前点走到这个点也是最短路,就直接加上即可。

     

    #include <iostream>
    #include <algorithm>
    #include <cstdio>
    #include <vector>
    #include <inttypes.h>
    #include <queue>
    #include <functional>
    #include <cstring>
    #define debug(x) std::cout << #x << " = " << x << std::endl;
    
    typedef long long int int_t;
    
    using std::cin;
    using std::endl;
    using std::cout;
    
    const int_t LARGE = 2000;
    const int_t INF = 0x7fffffff;
    struct Edge {
    	int_t to;
    	int_t weight;
    	Edge(int_t to = -1 ,int_t weight = 0) {
    		this -> to = to;
    		this -> weight = weight;
    	}
    	bool operator<(const Edge& x) const {
    		return to < x.to;
    	}
    	bool operator==(const Edge& x) const {
    		return to == x.to;
    	}
    };
    
    struct Pair {
    	int_t dis;
    	int_t vertex;
    	Pair(int_t dis,int_t vertex) {
    		this -> dis = dis;
    		this -> vertex = vertex;
    	}
    	bool operator>(const Pair& pair) const {
    		return dis > pair.dis;
    	}
    
    };
    
    std::vector<Edge> graph[LARGE + 1];
    
    void pushEdge(int_t from,int_t to,int_t weight) {
    	graph[from].push_back(Edge(to,weight));
    //    graph[to].push_back(Edge(from,weight));
    }
    bool visited[LARGE + 1];
    int_t dis[LARGE + 1];
    int_t count[LARGE + 1];
    int_t n,m;
    void Dijkstra() {
    	std::fill(dis + 1 ,dis + 1 + n,INF);
    	count[1] = 1;
    	dis[1] = 0;
    	std::priority_queue<Pair,std::vector<Pair>,std::greater<Pair> > heap;
    	heap.push(Pair(0,1));
    	while(heap.empty() == false) {
    		Pair top = heap.top();
    		heap.pop();
    		if(visited[top.vertex]) continue;
    //		cout<<"visiting "<<top.vertex<<endl;
    		visited[top.vertex] = true;
    		for(int_t i = 0; i < graph[top.vertex].size(); i++) {
    			Edge& edge = graph[top.vertex][i];
    			if(dis[edge.to] > dis[top.vertex] + edge.weight) {
    				dis[edge.to] = dis[top.vertex] + edge.weight;
    				heap.push(Pair(dis[edge.to],edge.to));
    				count[edge.to] = count[top.vertex];
    			} else if(dis[edge.to] == dis[top.vertex] + edge.weight) {
    				count[edge.to] += count[top.vertex];
    			}
    		}
    	}
    }
    int_t weight[LARGE + 1][LARGE + 1];
    int main() {
    	memset(weight,0x7f,sizeof(weight));
    	cin >> n >> m;
    	for(int_t i = 1; i <= m; i++) {
    		int_t from,to,weight;
    		cin >> from >> to >> weight;
    		if(weight < ::weight[from][to]){
    			pushEdge(from,to,weight);
    			::weight[from][to] = weight;
    		}
    		
    	}
    //	for(int_t i = 1; i <= n; i++) {
    //		std::sort(graph[i].begin(),graph[i].end());
    //		std::vector<Edge>& curr = graph[i];
    //		curr.resize(std::unique(curr.begin(),curr.end()) - curr.begin());
    //	}
    	Dijkstra();
    	if(dis[n] >= INF) {
    		cout <<"No answer";
    	} else
    		cout << dis[n] << " " << count[n] << endl;
    	return 0;
    }

     

  • LOJ2718 NOI2018 归程

    一开始我还真的以为是我常数太大…

    一句话题意:每次询问给定$vertex,alt$,规定海拔小于等于$alt$的边不连通时,$vertex$所在的联通块到$1$号点的最短距离。

    然后想一下,这玩意似乎可以用并查集维护?

    先跑一下1到所有点的最短路,这个等价于所有点到1的最短路

    把边按照海拔从大到小排序,然后一条一条边处理。

    每一条边加入等价于合并这个边两个端点所在的联通块的答案。

    然后把这个合并过程可持久化。

    然后一次海拔为$alt$的询问,就等价于合并完所有海拔大于$alt$的边之后,$vertex$所在的联通块到1的最短路。

    所以每次询问我们去找到这个合并后的并查集,然后输出答案即可。

    关于可持久化并查集的实现,这里不要使用主席树 (常数太大卡不过去的)

    可持久化并查集可以视为可持久化数组,在这道题中,我们需要实现的可持久化数组仅仅需要支持历史版本查询和对最新的版本进行修改(每一条边的合并总是基于上一条边合并后的版本)

    所以我们考虑另一种实现:

    假设我们要实现有n个数的可持久化数组,那么我们可以开n个vector,每当对数组中的一个元素进行修改时,我们就在这个元素对应的vector中加入一个元素表示最新的修改,然后查询一个元素的历史版本$v$时,直接在这个元素对应的vector中二分到最大的小于等于$v$的版本,然后返回那个版本的值即可。

    二分的常数不知道比主席树小到哪里去了。

    所以复杂度$O((N+M)logN+MlogN+QlogN)$.

    https://loj.ac/submission/204465

    代码

    // luogu-judger-enable-o2
    #include <iostream>
    #include <algorithm>
    #include <vector>
    #include <cstdio>
    #include <queue>
    using int_t = int;
    using std::cin;
    using std::cout;
    using std::endl;
    const int_t INF = 0x7fffffff;
    const int_t LARGE = 500000;
    //并查集中的一个元素
    struct State
    {
        int_t parent = -1;
        int_t dis = INF;
        int_t size;
        State(int_t p = -1, int_t d = INF, int_t size = 1)
        {
            this->parent = p;
            this->dis = d;
            this->size = size;
        }
        State generate(int_t parent)
        {
            State result = *this;
            result.parent = parent;
            return result;
        }
        State byDis(int_t dis, int_t size)
        {
            State result = *this;
            result.dis = dis;
            result.size = size;
            return result;
        }
    };
    //可持久化数组的实现
    struct PersistableArray
    {
        struct Pair
        {
            int_t version;
            State val;
            Pair(int_t v, const State &s)
            {
                version = v;
                val = s;
            }
            operator int_t()
            {
                return version;
            }
            bool operator<(const Pair &x)
            {
                return version < x.version;
            }
        };
        std::vector<Pair> items[LARGE + 1];
        int_t n;
        int_t version_n = 0;
        PersistableArray(int_t n, State *states)
        {
            this->n = n;
            for (int_t i = 1; i <= n; i++)
            {
                items[i].push_back(Pair{0, states[i]});
            }
        }
        int_t modify(int_t pos, const State &val)
        {
            items[pos].push_back(Pair{++version_n, val});
            return version_n;
        }
        State &query(int_t pos, int_t ver)
        {
            if (ver > items[pos].back())
                return items[pos].back().val;
            auto itr = std::lower_bound(items[pos].begin(), items[pos].end(), ver);
            while (*itr > ver)
                itr--;
            return (*itr).val;
        }
    };
    //在数组arr中,基于版本ver查询x对应的状态
    State query(PersistableArray *arr, int_t ver, int_t x)
    {
        State curr;
        while ((curr = arr->query(x, ver)).parent != x)
        {
            x = curr.parent;
        }
        return curr;
    }
    //合并基于base版本的v1和v2集合,返回新版本
    int_t merge(PersistableArray *arr, int_t base, int_t v1, int_t v2)
    {
        
        State s1 = query(arr, base, v1);
        State s2 = query(arr, base, v2);
        if(s1.parent==s2.parent) return base;
        if (s1.size > s2.size)
        {
            std::swap(s1, s2);
        }
        arr->modify(s1.parent, s1.generate(s2.parent));
        return arr->modify(s2.parent, s2.byDis(std::min(s1.dis, s2.dis), std::max(s1.size + 1, s2.size)));
    }
    
    struct Edge
    {
        int_t from;
        int_t to;
        int_t weight;
        int_t altitude;
        int_t version = -1;
        Edge(int_t from, int_t to, int_t weight, int_t altitude)
        {
            this->from = from;
            this->to = to;
            this->weight = weight;
            this->altitude = altitude;
        }
    
        operator int_t()
        {
            return altitude;
        }
    
        bool operator>(const Edge &edge)
        {
            return altitude > edge.altitude;
        }
    };
    
    struct Processor
    {
        std::vector<Edge> edges;
        // std::vector<int_t> numbers;
        std::vector<Edge> graph[LARGE + 1];
        int n, m;
        int q, k, s;
        int_t dis[LARGE + 1];
        bool visited[LARGE + 1];
        PersistableArray *arr;
    
        void process()
        {
    
            scanf("%d%d", &n, &m);
    
            edges.push_back(Edge(-1, -1, -1, INF));
            for (int_t i = 1; i <= m; i++)
            {
                int from, to, weight, alt;
                scanf("%d%d%d%d", &from, &to, &weight, &alt);
                edges.push_back(Edge(from, to, weight, alt));
                // numbers.push_back(alt);
                graph[from].push_back(Edge(from, to, weight, alt));
                graph[to].push_back(Edge(to, from, weight, alt));
            }
            std::sort(edges.begin(), edges.end(), [&](const Edge &a, const Edge &b) -> bool { return a.altitude < b.altitude; });
            Dijkstra();
            scanf("%d%d%d", &q, &k, &s);
            if (q == 0)
                return;
            static State inits[LARGE + 1];
            for (int_t i = 1; i <= n; i++)
            {
                inits[i] = State(i, dis[i]);
            }
            arr = new PersistableArray(n, inits);
            edges.back().version = 0;
            for (int_t i = edges.size() - 1; i >= 0; i--)
            {
                auto &edge = edges[i];
                if (i != edges.size() - 1)
                    edge.version = merge(arr, edges[i + 1].version, edge.from, edge.to);
            }
    
            int_t lastans = 0;
            for (int_t i = 1; i <= q; i++)
            {
                int vertex, alt;
                scanf("%d%d", &vertex, &alt);
                vertex = (vertex + k * lastans - 1) % n + 1;
                alt = (alt + lastans * k) % (s + 1);
                auto &edge = (*std::upper_bound(edges.begin(), edges.end(), alt));
                int_t result = query(arr, edge.version, vertex).dis;
                printf("%d\n", (int)result);
                lastans = result;
                // if (i % 800 == 0)
                //     cout << "query " << i << endl;
            }
        }
        void Dijkstra()
        {
            std::fill(dis + 1, dis + 1 + n, INF);
            std::fill(visited + 1, visited + 1 + n, false);
            dis[1] = 0;
            struct Pair
            {
                int_t vertex;
                int_t dis;
    
                Pair(int_t v, int_t d)
                {
                    vertex = v;
                    dis = d;
                }
            };
            const auto comp = [&](const Pair &a, const Pair &b) -> bool {
                return a.dis > b.dis;
            };
            std::priority_queue<Pair, std::vector<Pair>, decltype(comp)> heap(comp);
            heap.push(Pair{1, 0});
            while (heap.empty() == false)
            {
                auto front = heap.top();
                heap.pop();
                if (visited[front.vertex])
                    continue;
                visited[front.vertex] = true;
                for (Edge &edge : graph[front.vertex])
                {
                    if (dis[edge.to] > dis[edge.from] + edge.weight)
                    {
                        dis[edge.to] = dis[edge.from] + edge.weight;
                        heap.push(Pair{edge.to, dis[edge.to]});
                    }
                }
            }
        }
    };
    
    int main()
    {
        int T;
        scanf("%d", &T);
        for (int i = 1; i <= T; i++)
        {
            auto p = new Processor;
            p->process();
            delete p;
        }
        return 0;
    }