您的位置:首页 > 其它

Intel Code Challenge Elimination Round (Div.1 + Div.2, combined) F

2016-10-11 22:23 483 查看
题意:

有n个序列,,保证每个序列中最大的数字不超过m,每个序列长度为ki,出现的数字不重复,每个时刻,将每个序列最前面的那个数字拿出来,依此排列,组成一个数组,对于i∈[1,m],每次要求记录最长的全部由i组成的子段的长度,,然后,每个序列左移一位,即第i个位置的数变成第i-1个位置的数,第1个位置的数变成第n个位置。最后,对于每个数字,输出所有时刻中,能取到的最长子段的长度。

范围n,m <= 1E5,ki <= 40,∑ki <= 2E5,操作一共执行10^100次



solution:

这个10^100等于告诉你,,,每种情况都能试一次。。。

对于第i个序列的第j个数,它出现在数组中的时刻s,一定满足s ≡ j - 1 (mod ki),那么连续的相邻的序列中,我们都要取这个数字(因为这样才能满足想找的子段尽可能长),就相当于判断一个线性同余方程组是否有解。

先预处理出每个数字能有的期望连续区间。。

假设序列[i,j]都存在数字x,那么等于我们要判断从i到j的一系列方程组有无解。。

预处理t[i][j] = [i,i+2^j-1]组成的方程组,或标记其无解

那么对于每个位置i,不断地用倍增以及合并方程的手段尽可能地右移,找到最右端点即可

令len = j - i + 1,∑len = [b]∑ki <= 2E5[/b]

[b]因此总复杂度O(nlognlogLCM)[/b]

[b]事实上,,可以证(suan)明(chu)LCM这个东西,,小于10^16[/b]









[b]哦不,,,这题并没有那么容易。。就算上述过程全部正确也还是会wa。。。。[/b]

[b]事实上用扩展欧几里得算法求出合并方程所需要的逆元的时候,,这个逆元可能偏大,,然后一乘就爆long long了。。。[/b]

[b]观察式子ax + by = g,,,如果让a < b,那么算出来的x也能小一点。。。[/b]

[b]执行合并前特判一下防止爆long long。。。。。[/b]

[b]因为这个调了一整天。。。。。。。。。。。。。。。[/b]







[b]此题还有一种解法。。事实上,,出现的同余方程的种类并没有很多[/b]

[b]O(n^4logn)预处理每个方程和哪些冲突[/b]

[b]对于当前要处理的模方程组,,用头尾指针弄一下,,,既然你预处理了哪些会冲突。。[/b]

[b]瞎搞一下就行了[/b]#include<iostream>
#include<cstdio>
#include<cmath>
#include<vector>
#include<cstring>
#include<cstdlib>
using namespace std;

typedef long long LL;
const int maxn = 1E5 + 10;

struct data{
int num,pos,len;
data(){}
data(int num,int pos,int len): num(num),pos(pos),len(len){}
}d[maxn];

int n,m;
LL r[maxn][20],a[maxn][20];

vector <data> v[maxn];

void gcd(LL a,LL &x,LL b,LL &y,LL &g)
{
if (!b) {
x = 1;
y = 0;
g = a;
return;
}
gcd(b,y,a%b,x,g);
y -= x*(a/b);
}

void Merge(LL r1,LL a1,LL r2,LL a2,LL &R,LL &A)
{
LL x,y,g;
if (a1 > a2) swap(r1,r2),swap(a1,a2);
gcd(a2,x,a1,y,g);
if ((r1 - r2) % g) {A = -1; return;}
x = (r1 - r2)/g*(x%(a1/g))%(a1/g);
A = a1/g*a2;
R = (a2*x%A + r2 + A)%A;
}

void Pre_Work(int tot)
{
for (int i = 1; i <= tot; i++) r[i][0] = d[i].pos,a[i][0] = d[i].len;
for (int j = 1; j < 20; j++)
for (int i = 1; i <= tot; i++) {
if (i + (1<<j) - 1 > tot) {a[i][j] = -1; continue;}
int p = i + (1<<(j-1));
LL r1 = r[i][j-1],a1 = a[i][j-1];
LL r2 = r[p][j-1],a2 = a[p][j-1];
if (a1 == -1 || a2 == -1) {a[i][j] = -1; continue;}
LL R,A;
Merge(r1,a1,r2,a2,R,A);
r[i][j] = R;
a[i][j] = A;
}
}

int Work(int tot)
{
Pre_Work(tot);
int ret = 1;
for (int i = 1; i < tot; i++) {
int now = 1,pos = i + 1;
LL r1 = r[i][0],a1 = a[i][0];
for (int j = 19; j >= 0; j--) {
if (pos > tot) break;
if (a[pos][j] == -1) continue;
LL r2 = r[pos][j],a2 = a[pos][j];
LL R,A;
Merge(r1,a1,r2,a2,R,A);
if (A == -1) continue;
now += (1<<j);
pos += (1<<j);
r1 = R; a1 = A;
}
ret = max(ret,now);
}
return ret;
}

int Solve(int now)
{
int ret = 0,tot = 0;
for (int i = 0; i < v[now].size(); i++) {
data k = v[now][i];
if (!tot || d[tot].num + 1 == k.num) d[++tot] = k;
else ret = max(ret,Work(tot)),d[tot = 1] = k;
}
if (tot) ret = max(ret,Work(tot));
return ret;
}

int main()
{
//freopen("DMC.txt","r",stdin);
cin >> n >> m;
for (int i = 1; i <= n; i++) {
int k; scanf("%d",&k);
for (int j = 0; j < k; j++) {
int x; scanf("%d",&x);
v[x].push_back(data(i,j,k));
}
}
for (int i = 1; i <= m; i++)
printf("%d\n",Solve(i));
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐