2016.6.30模拟赛

T1

【题目来源】Codeforces Beta Round #72 Div1 E

【题解】

题目等价于最大化重叠长度。

最直接的想法,由于重叠部分只和后面$20$个字符有关,用$f(i,s1,s2)$表示考虑了前$i$个字符串,一个序列的最后$20$个点为$s1$,另一个序列最后$20$个点为$s2$.

这样很好转移,但是状态数是$2^{40}n$,无法接受。

我们注意到每个串长度相同,所以$s1,s2$一定是某个字符串,所以可以用$1..n$来代替$s1$, 这样状态数变为$n^3$。

再注意到,$i$这个点一定是某一个序列的结尾,所以只用$f(i,j)$表示即可,其中$j$表示另一个序列的结尾位置。这样状态数变为$n^2$。

状态数已经很优了,我们来优化一下转移。

转移方程:

$$f(i+1,j) = f(i,j) + calc(i,i+1) (j<i)$$

$$f(i+1,i) = \max \{f(i,k) + calc(k, i+1) (k<i) \} $$

第一种转移每个状态加上了相同的量,所以记录一个全局tag即可,第二个转移需要我们花$O(n)$的时间枚举,但我们注意到$calc(k,i+1)$最多只有$20$种可能,可以来枚举这个。当$d = calc(k, i+1)$确定之后,我们就要找一个字符串他的后$d$个字符与$i+1$的前$d$个字符相同,并且它的dp值最大,这样我们就可以维护$g(i,s)$表示后$i$位为$s$前面不定的最大dp值,这样就可以$O(20)$算出第二种转移。更新的时候我们也只需要花$O(20)$的时间去更新f就行了。时间复杂度$O(20n)$

最后,我们需要用$2^{20}$在最终的$g$里枚举一下,这也是可以接受的。

【code】

#include<bits/stdc++.h>

using namespace std;

const int N = 200000 + 10, INF = 0x3f3f3f3f;

int f[21][1 << 20], a[N], n, L;

int front(int x, int i) {
	return x >> (L - i);
}
int back(int x, int i) {
	return x & ((1 << i) - 1);
}

void input() {
	scanf("%d", &n);
	char s[99];
	for(int i = 0; i < n; i++) {
		scanf("%s", s);
		for(int j = 0; s[j]; j++) {
			(a[i] <<= 1) |= s[j] - '0';
		}
	}
	L = strlen(s);
}

int calc(int x, int y) {
	for(int i = L; i >= 0; i--) {
		if(back(x, i) == front(y, i)) return i;
	}
	abort();
}

void maxit(int &x, int y) {
	if(x < y) x = y;
}

void solve() {
	int tag = 0;
	memset(f, -0x3f, sizeof f);
	for(int i = 1; i < n; i++) {
		int res = tag; // [0,i-1]构成子序列
		for(int j = 0; j <= L; j++) {
			maxit(res, j + f[j][front(a[i], j)] + tag);
		}
		tag += calc(a[i-1], a[i]);
		res -= tag;
		for(int j = 0; j <= L; j++) {
			maxit(f[j][back(a[i-1], j)], res);
		}
	}
	int ans = 0;//这里来考虑有一个序列为空的情况
	for(int i = 0; i < (1 << L); i++) {
		maxit(ans, f[L][i]);
	}
	printf("%d\n", n * L - (ans + tag));
}

int main() {
	freopen("string.in", "r", stdin);
	freopen("string.out", "w", stdout);

	input();
	solve();

	return 0;
}

 

 

T2

【题目来源】Codeforces Abbyy Cup 2.0-Final B

【题意】

【题解】注意认真看题,能不能传递并不是由$i$决定的,而是由$j$决定的,所以要把$a$反过来读。

然后就很简单了,注意到如果从$s$能够走到$t$,那么对于$\forall s < p < t$,$s$都能够走到$t$。

所以倍长序列以后,求每个点最远能走到哪就可以了。

如果走一步,显然是走到$i + a[i]$这个位置。

如果走两步呢?那么会走到所有$j \in [i,i+a[i]]$里$j+a[j]$最大的点,设为$f(i)$

如果走三步呢?那么会走到所有$j \in [i,i+a[i]]$里$f(j)$最大的点。

于是,这样就可以倍增了。

对于一个起点,可以在$O(n \log n)$的时间里求出走到终点需要几步。

【notice】认真读题。。

【code】

 

#include<bits/stdc++.h>

using namespace std;

const int N = 500000 + 10, INF = 1 << 29;

int a[N], n, rgt[N];
int f[20][N];

typedef pair<int, int> pii;
#define FI first
#define SE second

struct SegmentTree {
	pii da[N * 4];

#define mid ((l + r) >> 1)
#define ls s << 1, l, mid
#define rs s << 1 | 1, mid + 1, r

	void modify(int s, int l, int r, int p, const pii &x) {
		if(l == r) return da[s] = x, void();
		if(p <= mid) modify(ls, p, x);
		else modify(rs, p, x);
		da[s] = max(da[s << 1], da[s << 1 | 1]);
	}

	pii query(int s, int l, int r, int L, int R){
		if(L <= l && r <= R) return da[s];
		pii res(0, 0);
		if(L <= mid) res = max(res, query(ls, L, R));
		if(mid < R) res = max(res, query(rs, L, R));
		return res;
	}
} seg;

int logn = 19;

int calc(int s) {
	int t = s + n - 1, ans = 2;
	if(rgt[s] >= t) return 1;
	for(int i = logn; i >= 0; i--) {
		if(rgt[f[i][s]] < t) {
			ans += 1 << i;
			s = f[i][s];
		}
	}
	return ans;
}

int main() {
	freopen("sequence.in", "r", stdin);
	freopen("sequence.out", "w", stdout);

	scanf("%d", &n);
	for(int i = n; i >= 1; i--) {
		scanf("%d", a + i);
	}
	for(int i = 1; i <= (n << 1); i++) {
		if(i > n) a[i] = a[i - n];
		rgt[i] = min(n << 1, i + a[i]);
	}

	for(int i = 1; i <= (n << 1); i++) {
		seg.modify(1, 1, n << 1, i, pii(rgt[i], i));
	}
	for(int i = 1; i <= (n << 1); i++) {
		f[0][i] = seg.query(1, 1, n << 1, i, rgt[i]).SE;
	}
	
	for(int i = 1; i <= logn; i++) {
		for(int j = 1; j <= (n << 1); j++) {
			f[i][j] = f[i-1][f[i-1][j]];
		}
	}

	/*
	for(int i = 0; i <= logn; i++) {
		printf("%d : ", i);
		for(int j = 1; j <= (n << 1); j++) {
			printf("%d%c", f[i][j], " \n"[j == (n<<1)]);
		}
	}
	*/

	long long ans = 0;
	for(int i = 1; i <= n; i++) {
		ans += calc(i);
	}
	cout << ans << endl;
	
	return 0;
}

 

T3

提交答案题。

旅行商问题,直接贪心走就能A了。。

 

posted @ 2016-06-30 16:29  Showson  阅读(220)  评论(0编辑  收藏  举报