• Luogu4156 WC2016 论战捆竹竿 KMP、同余类最短路、单调队列


    传送门


    豪华升级版同余类最短路……

    官方题解

    主要写几个小trick:

    (1.O(nm))实现同余类最短路:

    设某一条边长度为(x),那么我们选择一个点,在同余类上不断跳(x),可以形成一个环。

    显然只有在同一个环上的两点之间才可能通过(x)进行转移。我们选择环上答案最小的点,它一定不会在当次更新时被更新答案,所以直接从这个点开始依次遍历环上的所有点,每一个点尝试从前面的一个点更新答案。

    (2.)(mod n)的同余类最短路变为(mod d)的同余类最短路:

    令新的同余类最短路为(g_x),原同余类最短路为(f_x),那么首先令(g_{f_i mod d} leftarrow f_i),但是可能会有一些(g)没有被正确更新。在(f_x)中实际上还有默认的长度为(n)的边,那么在(g)中用长度为(n)的边在(g)上更新一次同余类最短路就可以得到正确的答案了。

    (3.)更新(border)长度为等差数列的一段数的操作过程:

    设这一个等差数列的首项为(x),公差为(y),有(t+1)项,先将原最短路变为(mod x)的同余类最短路,那么对于每一个环上的点,可以从前面(t)个点进行转移,代价为距离( imes y + x),本质是一个多重背包。与此同时类似trick1地,转移一定不会跨越一个环上的最小值点,所以可以破环成链变为多重背包问题,使用单调队列优化转移。

    PS:UOJ EX5好毒瘤啊……

    Update on 2019.12.28:重写了一遍终于过了UOJ的Ex test,特来还愿。下面的代码更新为可以通过UOJ的代码。

    #include<bits/stdc++.h>
    using namespace std;
    
    #define ll long long
    const int _ = 5e5 + 7;
    char str[_]; ll mn[_] , tmp[_] , W; int T , L , curl , nxt[_];
    template < typename T >
    void chkmin(T &a , T b){a = a < b ? a : b;}
    int upd(int x){return x + (x >> 31 & curl);}
    
    int num[_];
    void ext(int len){
    	int stp = len % curl; if(!stp) return;
    	memset(num , 0 , sizeof(int) * (curl + 1));
    	for(int i = 0 ; !num[i] ; ++i){
    		int x = i , id = i;
    		do{id = mn[id] > mn[x] ? x : id; x = upd(x + stp - curl); num[x] = 1;}while(x != i);
    		x = id; do{int p = upd(x + stp - curl); chkmin(mn[p] , mn[x] + len); x = p;}while(x != id);
    	}
    }
    
    ll val[_]; int que[_] , hd , tl;
    void ext1(int len , int tms){
    	int stp = len % curl; if(!stp) return;
    	memset(num , 0 , sizeof(int) * (curl + 1));
    	for(int i = 0 ; !num[i] ; ++i){
    		int x = i , id = i , cnt = 0;
    		do{id = mn[id] > mn[x] ? x : id; x = upd(x + stp - curl);}while(x != i);
    		x = id; hd = tl = 0;
    		do{
    			num[x] = ++cnt; if(hd != tl && cnt - num[que[hd]] > tms) ++hd;
    			if(hd != tl) chkmin(mn[x] , val[que[hd]] + 1ll * stp * cnt + curl);
    			val[x] = mn[x] - 1ll * stp * cnt;
    			while(hd != tl && val[que[tl - 1]] >= val[x]) --tl;
    			que[tl++] = x; x = upd(x + stp - curl);
    		}while(x != id);
    	}
    }
    
    void change(int nl){
    	memset(tmp , 0x3f , sizeof(ll) * max(tl , nl));
    	for(int i = 0 ; i < curl ; ++i) chkmin(tmp[mn[i] % nl] , mn[i]);
    	int tl = curl; curl = nl; memcpy(mn , tmp , sizeof(ll) * max(tl , nl)); ext(tl);
    }
    
    int main(){
    	nxt[0] = -1;
    	for(scanf("%d" , &T) ; T ; --T){
    		scanf("%d %lld %s" , &L , &W , str + 1); W -= L;
    		memset(mn , 0x3f , sizeof(mn)); mn[0] = 0;
    		for(int i = 1 ; i <= L ; ++i){
    			int t = nxt[i - 1];
    			while(~t && str[t + 1] != str[i]) t = nxt[t];
    			nxt[i] = t + 1;
    		}
    		vector < int > border; int t = curl = L , pos = 0;
    		while(nxt[t]) border.push_back(L - (t = nxt[t]));
    		while(pos < border.size())
    			if(pos + 2 >= border.size()) ext(border[pos++]);
    			else if(border[pos + 2] - border[pos + 1] == border[pos + 1] - border[pos]){
    				int r = pos + 2 , len = border[pos + 1] - border[pos];
    				while(r + 1 < border.size() && border[r + 1] - border[r] == len) ++r;
    				change(border[pos]); ext1(len , r - pos); pos = r + 1;
    			}
    			else ext(border[pos++]);
    		ll sum = 0;
    		for(int i = 0 ; i < curl ; ++i)
    			if(W >= mn[i]) sum += (W - mn[i]) / curl + 1;
    		cout << sum << endl;
    	}
    	return 0;
    }
    
  • 相关阅读:
    JAVA JDK配置
    jsoncpp的使用
    VS2015 +Qt5 串口工具
    Unable to convert MySQL date/time value to System.DateTime问题解决方案
    datagridview的一些设置
    C# 如何使用长度来切分字符串
    (备忘)打开office2010总是在配置进度
    (备忘)卸载微软自带输入法
    (备忘)怎么去除WinRAR弹窗广告?
    winfrom弹出窗口用timer控件控制倒计时20秒后关闭
  • 原文地址:https://www.cnblogs.com/Itst/p/10251021.html
Copyright © 2020-2023  润新知