BZOJ2093: [Poi2010]Frog

此题倍增比较好想

找第 k 近的点感觉不是很好想

没有想到啊——

两个指针扫维护里当前点最近的 k + 1 个点, +1 是因为还要算上当前点
考虑当 i + 1 时 l,r 的移动,为了保证最小,要把右端点+1的点和左端点进行比较,
若 pos[r + 1] - pos[i] < pos[i] - pos[l],就平移整个区间

此时可以选择倍增,需要滚动数组,就每次倍增的时候不断更新
ans[i] 表示从第 i 个点出发,当前跳到了 ans[i], 每次更新跳了 2^i 跳到哪
总共更新 log(m) 次就好了

或者跑个置换(?)快速幂就好了

听说以后polya要用就当板子写的

感觉思想和倍增差不多?

 

在 BZOJ 上加了输优才过。。。

比倍增还慢。。


代码:

快速幂:

#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<cstdio>
using namespace std;

typedef long long ll;
const int MAXN = 1000005;

int n, k;
ll m, pos[MAXN];
struct TRN{
	int arr[MAXN];
	int& operator [] (int x) {
		return arr[x];
	}
	void operator *= (TRN &b) {
		static TRN tmp;
		for(int i = 1; i <= n; ++i) 
			tmp[i] = b[arr[i]];
		memcpy(this, &tmp, sizeof(TRN));
		return;
	}
}to, ans;

inline int rd() {
	register int x = 0;
	register char c = getchar();
	while(!isdigit(c)) c = getchar();
	while(isdigit(c)) {
		x = x * 10 + (c ^ 48);
		c = getchar();
	}
	return x;
}
inline ll rdll() {
	register ll x = 0ll;
	register char c = getchar();
	register bool f = false;
	while(!isdigit(c)) {
		f = (c == '-');
		c = getchar();
	}
	while(isdigit(c)) {
		x = x * 10ll + (c ^ 48);
		c = getchar();
	}
	return f ? -x : x;
}
inline void write(int x) {
	register int y = 10, len = 1;
	while(y <= x) {y *= 10; ++len;}
	while(len--) {y /= 10; putchar(x / y + 48); x %= y;}
	return;
}
inline void fastpow(TRN &t, ll top) {
	while(top) {
		if(top & 1ll) ans *= t;
		t *= t;
		top >>= 1;
	}
	return;
}

int main() {
	n = rd(); k = rd(); m = rdll();
	for(int i = 1; i <= n; ++i) 
		pos[i] = rdll();
	int l = 1, r = k + 1;
	for(int i = 1; i <= n; ++i) {
		while((r < n) && (pos[r + 1] - pos[i] < pos[i] - pos[l])) {++l; ++r;}
		to[i] = ((pos[i] - pos[l] >= pos[r] - pos[i]) ? (l) : (r));
		ans[i] = i;
	}
	fastpow(to, m);
	for(int i = 1; i < n; ++i) {
		write(ans[i]);
		putchar(' ');
	}
	write(ans[n]);
	putchar('\n');
	return 0;
}

倍增:

#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cstdio>
#include<cmath>
using namespace std;

typedef long long ll;
const int MAXN = 1000005;

int n, k, lg;
int to[2][MAXN], ans[MAXN];
ll m, pos[MAXN], pwr[70];

inline int rd() {
	register int x = 0;
	register char c = getchar();
	while(!isdigit(c)) c = getchar();
	while(isdigit(c)) {
		x = x * 10 + (c ^ 48);
		c = getchar();
	}
	return x;
}
inline ll rdll() {
	register ll x = 0ll;
	register char c = getchar();
	register bool f = false;
	while(!isdigit(c)) {
		f = (c == '-');
		c = getchar();
	}
	while(isdigit(c)) {
		x = x * 10ll + (c ^ 48);
		c = getchar();
	}
	return f ? -x : x;
}

int main() {
	n = rd(); k = rd(); m = rdll();
	lg = log2(m) + 1;
	pwr[0] = 1ll;
	for(int i = 1; i <= lg; ++i) pwr[i] = (pwr[i - 1] << 1);
	for(int i = 1; i <= n; ++i) pos[i] = rdll();
	int l = 1, r = k + 1;
	for(int i = 1; i <= n; ++i) {
		while(r < n && pos[r + 1] - pos[i] < pos[i] - pos[l]) {++l; ++r;}
		to[0][i] = ((pos[i] - pos[l] >= pos[r] - pos[i]) ? (l) : (r));
	}
	if(m & 1) for(int i = 1; i <= n; ++i) ans[i] = to[0][i];
	else for(int i = 1; i <= n; ++i) ans[i] = i;
	int cur = 1;
	for(int i = 1; i <= lg; ++i) {
		for(int j = 1; j <= n; ++j) to[cur][j] = to[cur ^ 1][to[cur ^ 1][j]];
		if((m & pwr[i])) for(int j = 1; j <= n; ++j) ans[j] = to[cur][ans[j]];
		cur ^= 1;
	}
	for(int i = 1; i < n; ++i) printf("%d ", ans[i]);
	printf("%d\n", ans[n]);
	return 0;
}
posted @ 2018-10-08 08:48  EvalonXing  阅读(152)  评论(0编辑  收藏  举报