zoukankan      html  css  js  c++  java
  • Loj #2687

    搞了一个 (nk^2) 的做法。

    首先如果前面是一个 (e) 那下一步一定是将 (e) 删掉,花费 $2$ 。

    那么可以将 (e) 删掉,每个之前有 (e) 的点都必须经过,求最短的方案。

    答案一定是向后走一段,然后把前面的所有必经点都走掉,于是可以轻松得到 (O(n^2)) 的做法。

    现在考虑优化。

    很容易想到的是每次只向后走一个,然后立即将前面所有必经点走掉。如果这个结论正确那状态是 (nk) 的,很容易转移。

    然而需要注意这里向后走的费用是 $2$,于是类似:

    [ egin{aligned} &ca dots aaba\ &01 dots 1011 end{aligned} ]

    这样的数据可以卡掉。

    但是我们可以考虑一个更平凡的结论:

    • 考虑每次向后一段再向前,再向后一次,最终到达的那一个节点一定是第一次到达。

    证明:例如:

    [ egin{matrix} a & o & o & b & o & c \ & d & leftarrow &leftarrow &leftarrow&leftarrow \ & & o & o &e end{matrix} ]

    我们显然可以改为:

    [ egin{matrix} a & o & o & b & & c \ & d & leftarrow & && \ & o & o & o &e \ & & & b+1 & leftarrow \ & & & o& o & c end{matrix} ]

    这样的话可以发现答案变小。

    于是最终答案一定可以分为若干段,每一段从左边一直跳到右边,然后退回左边,并且两段之间无交。

    于是我们考虑以下状态: (f(i,j)) 表示当前在 (i),已经从 (j) 走到 (i) 的答案。

    (g(i,j)) 表示当前在 (j),上一段的最右端是 (i) 的答案。

    我们每次改变较小的还要改变的位置。

    考虑转移有哪些:

    第一种是 (f(i,j) o g(j, ext{next}(i))) 即下一段第一个经过的是 (j)

    第二种是 (g(i,j) o f( ext{first}(i+1,j),j)) 即下一段退回到 (i+1,j) 这段的第一个必经点。

    第三种是 (g(i,j) o g( ext{next}(i),j)) 即上一段延长了。

    不难发现 (j) 就是 (i) 或者 (i) 的后继节点,于是状态是 (nk) 的,转移 (O(k)) ,于是总复杂度 (O(nk^2))

    需要注意特判以下最后一段以及 (i=j) 的转移。

    代码

    #include<bits/stdc++.h>
    using namespace std;
    const int N = 7e4+5;
    const int table[10]={0,1,2,3,-1,4,5,6,7,8};
    int n;char s[N];
    bool mark[N];
    int ch[N],nxt[N][9],Last[N],f[N][9],g[N][9],Mark[N],h[N];
    int ans=0x3f3f3f3f;
    
    inline void Doit(int S){
    	for(int y=0;y<9;y++)if(nxt[S][y]&&f[S][y]!=0x3f3f3f3f){
    		if(Mark[nxt[S][y]+1]==0){
    			ans=min(ans,f[S][y]);continue;
    		}else{
    			for(int j=0;j<9;j++)if(nxt[nxt[S][y]+1][j]){
    				int ny = nxt[nxt[S][y]+1][j];
    				ans=min(ans,f[S][y]+h[ny]+ny-nxt[S][y]);
    			}
    		}
    		for(int y2=0;y2<9;y2++)if(nxt[S+1][y2]){
    			int to1=nxt[S][y], to2=nxt[S+1][y2];
    			if(to2>=to1){
    				int nxt = Mark[to1+1];nxt=min(nxt,to2);
    				f[nxt][y2]=min(f[nxt][y2],to2-nxt+f[S][y]+2);
    			}
    			//if(to2<=to1) f[to2][y]=min(f[to2][y],2+f[S][y]);
    		}
    		int to1=nxt[S][y];
    		if(to1==S){
    			for(int y2=0;y2<9;y2++)if(nxt[S+1][y2]){
    				for(int y3=0;y3<9;y3++)if(nxt[S+1][y3]){
    					int to2=nxt[S+1][y2],to3=nxt[S+1][y3];
    					if(to2<to3)g[to2][y3]=min(g[to2][y3],f[S][y]+2+2+to2-S);
    				}
    			}
    		}
    		for(int y2=0;y2<9;y2++)if(nxt[S+1][y2]){
    			int to2=nxt[S+1][y2];
    			if(to2>=to1)g[to1][y2]=min(g[to1][y2],f[S][y]+2);
    		}
    	}
    	for(int y=0;y<9;y++)if(nxt[S][y]&&g[S][y]!=0x3f3f3f3f){
    		for(int y2=0;y2<9;y2++)if(nxt[S+1][y2]){
    			int to1=nxt[S][y], to2=nxt[S+1][y2];
    			if(to2<=to1)g[to2][y]=min(2+to2-S+g[S][y],g[to2][y]);
    		}
    		int _nxt=min(Mark[S+1],nxt[S][y]);
    		f[_nxt][y]=min(f[_nxt][y],g[S][y]+nxt[S][y]-_nxt);
    	}
    }
    
    int main()
    {
    	cin >> n;
    	scanf("%s",s+1);
    	int _n=0;
    	int ans2=0;
    	for(int i=1;i<=n;i++){
    		if(s[i]=='e'){ans2+=2;continue;}
    		else ch[++_n]=table[s[i]-'a'],mark[_n]=s[i-1]=='e';
    	}
    	mark[1]=1;n=_n;
    	for(int i=n;i;i--){
    		Mark[i]=mark[i]?i:Mark[i+1];
    		Last[ch[i]]=i;
    		for(int j=0;j<9;j++)nxt[i][j]=Last[j];
    	}
    	for(int i=n;i;i--){
    		h[i]=0x3f3f3f3f;if(!Mark[i+1])h[i]=2;
    		for(int j=0;j<9;j++)if(nxt[i+1][j]){
    			int nx=nxt[i+1][j];
    			if(nx)h[i]=min(h[i],h[nx]+2+nx-i);
    		}
    	}
    	for(int i=1;i<=n;i++)for(int j=0;j<9;j++){g[i][j] = f[i][j] = 0x3f3f3f3f;}
    	f[1][ch[1]]=0;
    	for(int i=1;i<=n;i++)Doit(i);
    	cout << ans + ans2 << endl;
    }
    
  • 相关阅读:
    生成函数解决多重集合的计数问题
    kmp板子
    poj1001
    【题解】洛谷P1315 [NOIP2011TG] 观光公交(前缀和+贪心)
    【题解】洛谷P1941 [NOIP2014TG] 飞扬的小鸟(背包DP)
    【题解】洛谷P2679 [NOIP2015TG] 子串(DP+滚动数组)
    【题解】洛谷P1514 [NOIP2010TG] 引水入城(DFS+DP)
    【题解】洛谷P1052 [NOIP2005TG] 过河(DP+离散化)
    [arc063F]Snuke's Coloring 2-[线段树+观察]
    [agc001E]BBQ Hard[组合数性质+dp]
  • 原文地址:https://www.cnblogs.com/weiyanpeng/p/12194639.html
Copyright © 2011-2022 走看看