1. 程式人生 > 實用技巧 >Gym 101239E BZOJ 4110 Evolution in Parallel (DP、結論)

Gym 101239E BZOJ 4110 Evolution in Parallel (DP、結論)

題目連結

(Gym) https://codeforces.com/gym/101239
(BZOJ) 大人,時代變了。

題解

這題好神仙啊
首先有一個顯然的 DP,按長度從小到大排序,維護一下目前可選的方案中除了 \(i\) 所在的組之外的組的最後一個元素都有哪些可能性。在 \(O(L)\) 時間內判斷一個串是否是另一個的子序列,時間複雜度 \(O(n^2L)\).
但是這樣並不能過,有一個很神仙的結論是,任何一個時刻有用的可選的方案只有至多 \(2\) 種!
這可以用數學歸納法證明。最一開始顯然只有一種可選方案。
如果某一時刻只有一種可選方案(不妨設為 \((i-1,x)\)),那麼下一個時刻只有可能出現 \((i,x)\)

\((i,i-1)\) 這兩種。
如果某一時刻有兩種可選方案(不妨設為 \((i-1,x)\)\((i-1,y)\)):
如果 \((i-1)\)\(i\) 的子序列,那麼 \((i,x)\)\((i,y)\) 都會成為候選集合。這時 \((i,i-1)\) 還有可能成為候選集合,但是我們注意到如果 \((i,i-1)\) 成為了候選集合,那麼 \(x\)\(y\) 至少有一個是 \((i-1)\) 的子序列,這意味著 \((i,i-1)\) 可以被 \((i,x)\)\((i,y)\) 完全替代!所以我們可以捨棄 \((i,i-1)\) 這種可能性,可選集合最多還是隻有 \(2\)
個。
否則,可選集合只可能有 \((i,i-1)\) 這一種。
所以轉移就可以了,時間複雜度 \(O(nL)\).

程式碼

#include<bits/stdc++.h>
#define llong long long
#define mkpr make_pair
#define x first
#define y second
#define iter iterator
#define riter reverse_iterator
#define y1 Lorem_ipsum_
#define tm dolor_sit_amet_
#define pii pair<int,int>
using namespace std;

inline int read()
{
	int x = 0,f = 1; char ch = getchar();
	for(;!isdigit(ch);ch=getchar()) {if(ch=='-') f = -1;}
	for(; isdigit(ch);ch=getchar()) {x = x*10+ch-48;}
	return x*f;
}

const int mxN = 4000;
int n;
string a[mxN+3];
vector<pii> s[mxN+3];
vector<int> ans[2];

bool cmp_len(string s,string t) {return s.length()<t.length();}

bool judge(int u,int v)
{
	for(int i=0,j=0; i<a[u].length(); i++)
	{
		while(j<a[v].length()&&a[v][j]!=a[u][i]) {j++;}
		if(j==a[v].length()) {return false;} j++;
	}
	return true;
}

int main()
{
	n = read(); cin>>a[n+1];
	for(int i=1; i<=n; i++) cin>>a[i];
	sort(a+1,a+n+1,cmp_len);
	for(int i=1; i<=n; i++) if(!judge(i,n+1)) {puts("impossible"); return 0;}
	s[0].push_back(mkpr(0,0));
	for(int i=1; i<=n; i++)
	{
		if(s[i-1].size()==1)
		{
			if(judge(i-1,i)) {s[i].push_back(mkpr(s[i-1][0].x,i-1));}
			if(i-1!=s[i-1][0].x&&judge(s[i-1][0].x,i)) {s[i].push_back(mkpr(i-1,s[i-1][0].x));}
		}
		else if(s[i-1].size()==2)
		{
			if(judge(i-1,i)) {s[i].push_back(mkpr(s[i-1][0].x,i-1)); s[i].push_back(mkpr(s[i-1][1].x,i-1));}
			else if(judge(s[i-1][0].x,i)) {s[i].push_back(mkpr(i-1,s[i-1][0].x));}
			else if(judge(s[i-1][1].x,i)) {s[i].push_back(mkpr(i-1,s[i-1][1].x));}
		}
		if(!s[i].size()) {puts("impossible"); return 0;}
	}
	for(int i=n,j=s[n][0].x,x=0; i>=1; i--)
	{
		ans[x].push_back(i);
		for(int k=0; k<s[i].size(); k++) if(s[i][k].x==j)
		{
			if(s[i][k].x==i-1) {x^=1; j = s[i][k].y;}
			else {j = s[i][k].x;}
			break;
		}
	}
	printf("%d %d\n",ans[0].size(),ans[1].size());
	for(int i=ans[0].size(); i>=1; i--) cout<<a[ans[0][i-1]]<<endl;
	for(int i=ans[1].size(); i>=1; i--) cout<<a[ans[1][i-1]]<<endl;
	return 0;
}