您的位置:首页 > 大数据 > 人工智能

Codeforces Round #209 (Div. 2) D. Pair of Numbers

2013-11-12 17:28 369 查看
这题的技巧貌似非常经典,但是但是比赛的时候不会,所以没做出来,后来前天下午做了一下福州大学的有奖月赛,其中卡在一题上2.5小时,终于想出来了这个技巧,(完全自己YY),后来和同学交流发现这种技巧很经典,很常用,然后有点小开心,(哎高中没搞过OI,就是这么蛋疼,这个技巧都得自己YY。。。有个人点你一下也许会少花点时间,不过自己想出来的总是会更有成就感,理解也更透彻啦。。。)顺便联想到这题可以用这个技巧做,所以写篇日志记录一下。

题目链接:http://codeforces.com/contest/359/problem/D

说的是让你求一个长度最长的区间,使得这个区间里的每个数都能被这个区间里的某个数整除。n = 300000。

技巧就是快速预处理出对于每个数,以他作为那个除数,向左右两边延伸最远能到的区间的两端的下标值。这样再O(n)一遍求出区间最大值后面就简单了。

关键是如何很快预处理出来?普通方法是从这个数开始向两边尝试,显然会TLE。(所有数相同则退化到O(n^2))

我们考虑递推,(我当时就是这么直接想到了递推的,可能有点突兀,可能和我那几天在做KMP有关,KMP也是递推的思想很巧妙,所以吸收了一下),如何递推呢?我们分开处理左端点值和右端点值,设两个数组 l[i], r[i],假设在处理 l[i]前面的 l[x]都已经处理好了,那么首先看当前的 a[i] 能否被 a[i - 1]整除,如果不能那么l[i] 等于 i - 1,如果能被整除这时候前面的结果就有用了,首先从 i - 1往左到 l[i - 1] 区间里的这些数都能被 a[i]整除,(这应该很显然吧?)但是
a[l[i - 1]]不能被 a[i - 1] 整除,所以不知道a[i] 与 a[l[i - 1]]的整除关系,这时候再判断一次整除关系就可以了,这样我们就跳过了一些数的整除性的判断,算法时间复杂度下降了很多。如果有个数轴,你看到的应该就是我们在跳跃着判断一些数的整除关系,判断次数减少很多,对于右端点,我们也类似处理,唯一有点小变化就是从右往左进行,这应该不难理解吧?

最后代码如下:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cctype>
#include <set>
#include <list>
#include <stack>
#include <queue>
#include <deque>
#include <string>
#include <vector>
#include <map>
#include <iomanip>
#include <ctime>
#pragma warning (disable : 4996)
#define mem(a) memset(a, 0, sizeof(a))
#define dou double
#define LL long long
#define N 311111
#define Mod 1000000007
#define sl(a) strlen(a)
#define eps 1e-8
#define inf 1000000000
using namespace std;

int a
, l
, r
, cnt
;

int main(){
int n, t, ca = 1, i, j, k, re, sum, tem, id = -1, st;
//freopen("in,txt", "r", stdin);
//freopen("out.txt", "w", stdout);
ios :: sync_with_stdio(false);

cin >> n;
for (i = 1; i <= n; ++i) cin >> a[i], l[i] = i - 1, r[i] = i + 1;
for (i = 1; i <= n; ++i){
tem = i;
while (tem && a[l[tem]] % a[i] == 0) tem = l[tem];
l[i] = l[tem];
}
for (i = n, r[n + 1] = n + 1; i >= 1; --i){
tem = i;
while (tem <= n && a[r[tem]] % a[i] == 0) tem = r[tem];
r[i] = r[tem];
}
//for (i = 1; i <= n; ++i) cout << l[i] << ' ';
//cout << endl;
//for (i = 1; i <= n; ++i) cout << r[i] << ' ';
//cout << endl;
for (i = 1; i <= n; ++i){
if (r[i] - l[i] > id) id = r[i] - l[i], st = l[i] + 1;
}
cnt[0] = st;
for (i = st, j = 1; i <= n; ++i){
if (r[i] - l[i] == id && l[i] + 1 > st) cnt[j++] = l[i] + 1, st = l[i] + 1;
}
cout << j << ' ' << id - 2 << endl;
for (i = 0; i < j; ++i) cout << cnt[i] << ' ';

return 0;
}

这样做的时间复杂度是多少,我不会算,我猜是O(n)的(会不会是O(nlogn)?)有人能告诉我么?

另外是福州大学有奖月赛的这题,启发我的一题:http://acm.fzu.edu.cn/contest/problem.php?cid=130&sortid=5

这题除了这个小技巧之外还需要一个技巧的,当时也自己YY的,貌似和标准解法不一样。值得一做,不难啦。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  CF codeforces